diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e65edd09d..afdac987c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,7 +11,7 @@ on: pull_request: env: - dotnet_sdk_version: '10.0.100-alpha.1.24620.13' + dotnet_sdk_version: '10.0.100-alpha.1.25059.31' postgis_version: 3 DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 0423de65a..321dbab66 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -27,7 +27,7 @@ on: - cron: '30 22 * * 6' env: - dotnet_sdk_version: '10.0.100-alpha.1.24620.13' + dotnet_sdk_version: '10.0.100-alpha.1.25059.31' jobs: analyze: diff --git a/Directory.Packages.props b/Directory.Packages.props index 7698fb554..c20177900 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -1,7 +1,7 @@ - 10.0.0-alpha.1.24620.1 - 10.0.0-alpha.1.24616.1 + 10.0.0-alpha.1.25070.1 + 10.0.0-alpha.1.25068.1 9.0.2 diff --git a/global.json b/global.json index d3a56aff6..4f8676115 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "10.0.100-alpha.1.24620.13", + "version": "10.0.100-alpha.1.25059.31", "rollForward": "latestMajor", "allowPrerelease": true } diff --git a/test/EFCore.PG.FunctionalTests/BulkUpdates/ComplexTypeBulkUpdatesNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/BulkUpdates/ComplexTypeBulkUpdatesNpgsqlTest.cs index e9bc9cac7..006b27647 100644 --- a/test/EFCore.PG.FunctionalTests/BulkUpdates/ComplexTypeBulkUpdatesNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/BulkUpdates/ComplexTypeBulkUpdatesNpgsqlTest.cs @@ -29,8 +29,10 @@ public override async Task Update_property_inside_complex_type(bool async) AssertExecuteUpdateSql( """ +@p='12345' + UPDATE "Customer" AS c -SET "ShippingAddress_ZipCode" = 12345 +SET "ShippingAddress_ZipCode" = @p WHERE c."ShippingAddress_ZipCode" = 7728 """); } @@ -41,8 +43,10 @@ public override async Task Update_property_inside_nested_complex_type(bool async AssertExecuteUpdateSql( """ +@p='United States Modified' + UPDATE "Customer" AS c -SET "ShippingAddress_Country_FullName" = 'United States Modified' +SET "ShippingAddress_Country_FullName" = @p WHERE c."ShippingAddress_Country_Code" = 'US' """); } @@ -53,10 +57,12 @@ public override async Task Update_multiple_properties_inside_multiple_complex_ty AssertExecuteUpdateSql( """ +@p='54321' + UPDATE "Customer" AS c -SET "BillingAddress_ZipCode" = 54321, +SET "Name" = c."Name" || 'Modified', "ShippingAddress_ZipCode" = c."BillingAddress_ZipCode", - "Name" = c."Name" || 'Modified' + "BillingAddress_ZipCode" = @p WHERE c."ShippingAddress_ZipCode" = 7728 """); } @@ -67,8 +73,10 @@ public override async Task Update_projected_complex_type(bool async) AssertExecuteUpdateSql( """ +@p='12345' + UPDATE "Customer" AS c -SET "ShippingAddress_ZipCode" = 12345 +SET "ShippingAddress_ZipCode" = @p """); } @@ -78,9 +86,11 @@ public override async Task Update_multiple_projected_complex_types_via_anonymous AssertExecuteUpdateSql( """ +@p='54321' + UPDATE "Customer" AS c -SET "BillingAddress_ZipCode" = 54321, - "ShippingAddress_ZipCode" = c."BillingAddress_ZipCode" +SET "ShippingAddress_ZipCode" = c."BillingAddress_ZipCode", + "BillingAddress_ZipCode" = @p """); } @@ -97,20 +107,20 @@ public override async Task Update_complex_type_to_parameter(bool async) AssertExecuteUpdateSql( """ -@complex_type_newAddress_AddressLine1='New AddressLine1' -@complex_type_newAddress_AddressLine2='New AddressLine2' -@complex_type_newAddress_Tags={ 'new_tag1', 'new_tag2' } (DbType = Object) -@complex_type_newAddress_ZipCode='99999' (Nullable = true) -@complex_type_newAddress_Code='FR' -@complex_type_newAddress_FullName='France' +@complex_type_p_AddressLine1='New AddressLine1' +@complex_type_p_AddressLine2='New AddressLine2' +@complex_type_p_Tags={ 'new_tag1', 'new_tag2' } (DbType = Object) +@complex_type_p_ZipCode='99999' (Nullable = true) +@complex_type_p_Code='FR' +@complex_type_p_FullName='France' UPDATE "Customer" AS c -SET "ShippingAddress_AddressLine1" = @complex_type_newAddress_AddressLine1, - "ShippingAddress_AddressLine2" = @complex_type_newAddress_AddressLine2, - "ShippingAddress_Tags" = @complex_type_newAddress_Tags, - "ShippingAddress_ZipCode" = @complex_type_newAddress_ZipCode, - "ShippingAddress_Country_Code" = @complex_type_newAddress_Code, - "ShippingAddress_Country_FullName" = @complex_type_newAddress_FullName +SET "ShippingAddress_AddressLine1" = @complex_type_p_AddressLine1, + "ShippingAddress_AddressLine2" = @complex_type_p_AddressLine2, + "ShippingAddress_Tags" = @complex_type_p_Tags, + "ShippingAddress_ZipCode" = @complex_type_p_ZipCode, + "ShippingAddress_Country_Code" = @complex_type_p_Code, + "ShippingAddress_Country_FullName" = @complex_type_p_FullName """); } @@ -120,12 +130,12 @@ public override async Task Update_nested_complex_type_to_parameter(bool async) AssertExecuteUpdateSql( """ -@complex_type_newCountry_Code='FR' -@complex_type_newCountry_FullName='France' +@complex_type_p_Code='FR' +@complex_type_p_FullName='France' UPDATE "Customer" AS c -SET "ShippingAddress_Country_Code" = @complex_type_newCountry_Code, - "ShippingAddress_Country_FullName" = @complex_type_newCountry_FullName +SET "ShippingAddress_Country_Code" = @complex_type_p_Code, + "ShippingAddress_Country_FullName" = @complex_type_p_FullName """); } @@ -151,13 +161,20 @@ public override async Task Update_complex_type_to_inline_without_lambda(bool asy AssertExecuteUpdateSql( """ +@complex_type_p_AddressLine1='New AddressLine1' +@complex_type_p_AddressLine2='New AddressLine2' +@complex_type_p_Tags={ 'new_tag1', 'new_tag2' } (DbType = Object) +@complex_type_p_ZipCode='99999' (Nullable = true) +@complex_type_p_Code='FR' +@complex_type_p_FullName='France' + UPDATE "Customer" AS c -SET "ShippingAddress_AddressLine1" = 'New AddressLine1', - "ShippingAddress_AddressLine2" = 'New AddressLine2', - "ShippingAddress_Tags" = ARRAY['new_tag1','new_tag2']::text[], - "ShippingAddress_ZipCode" = 99999, - "ShippingAddress_Country_Code" = 'FR', - "ShippingAddress_Country_FullName" = 'France' +SET "ShippingAddress_AddressLine1" = @complex_type_p_AddressLine1, + "ShippingAddress_AddressLine2" = @complex_type_p_AddressLine2, + "ShippingAddress_Tags" = @complex_type_p_Tags, + "ShippingAddress_ZipCode" = @complex_type_p_ZipCode, + "ShippingAddress_Country_Code" = @complex_type_p_Code, + "ShippingAddress_Country_FullName" = @complex_type_p_FullName """); } @@ -208,8 +225,10 @@ public override async Task Update_collection_inside_complex_type(bool async) AssertExecuteUpdateSql( """ +@p={ 'new_tag1', 'new_tag2' } (DbType = Object) + UPDATE "Customer" AS c -SET "ShippingAddress_Tags" = ARRAY['new_tag1','new_tag2']::text[] +SET "ShippingAddress_Tags" = @p """); } diff --git a/test/EFCore.PG.FunctionalTests/BulkUpdates/NonSharedModelBulkUpdatesNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/BulkUpdates/NonSharedModelBulkUpdatesNpgsqlTest.cs index 6738856aa..af5a00965 100644 --- a/test/EFCore.PG.FunctionalTests/BulkUpdates/NonSharedModelBulkUpdatesNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/BulkUpdates/NonSharedModelBulkUpdatesNpgsqlTest.cs @@ -53,8 +53,10 @@ public override async Task Replace_ColumnExpression_in_column_setter(bool async) AssertSql( """ +@p='SomeValue' + UPDATE "OwnedCollection" AS o0 -SET "Value" = 'SomeValue' +SET "Value" = @p FROM "Owner" AS o WHERE o."Id" = o0."OwnerId" """); @@ -73,8 +75,10 @@ public override async Task Update_non_owned_property_on_entity_with_owned(bool a AssertSql( """ +@p='SomeValue' + UPDATE "Owner" AS o -SET "Title" = 'SomeValue' +SET "Title" = @p """); } @@ -111,8 +115,10 @@ public override async Task Update_non_owned_property_on_entity_with_owned_in_joi AssertSql( """ +@p='NewValue' + UPDATE "Owner" AS o -SET "Title" = 'NewValue' +SET "Title" = @p FROM "Owner" AS o0 WHERE o."Id" = o0."Id" """); @@ -125,8 +131,8 @@ public override async Task Update_owned_and_non_owned_properties_with_table_shar AssertSql( """ UPDATE "Owner" AS o -SET "OwnedReference_Number" = length(o."Title")::int, - "Title" = COALESCE(o."OwnedReference_Number"::text, '') +SET "Title" = COALESCE(o."OwnedReference_Number"::text, ''), + "OwnedReference_Number" = length(o."Title")::int """); } @@ -169,8 +175,8 @@ public override async Task Update_non_main_table_in_entity_with_entity_splitting AssertSql( """ UPDATE "BlogsPart1" AS b0 -SET "Rating" = length(b0."Title")::int, - "Title" = b0."Rating"::text +SET "Title" = b0."Rating"::text, + "Rating" = length(b0."Title")::int FROM "Blogs" AS b WHERE b."Id" = b0."Id" """); @@ -252,8 +258,10 @@ public override async Task Update_with_view_mapping(bool async) AssertSql( """ +@p='Updated' + UPDATE "Blogs" AS b -SET "Data" = 'Updated' +SET "Data" = @p """); } diff --git a/test/EFCore.PG.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesNpgsqlTest.cs index 8ac9c9c04..50cb15039 100644 --- a/test/EFCore.PG.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesNpgsqlTest.cs @@ -508,9 +508,33 @@ LIMIT @p0 OFFSET @p """); } - public override async Task Delete_with_left_join(bool async) + public override async Task Delete_with_LeftJoin(bool async) { - await base.Delete_with_left_join(async); + await base.Delete_with_LeftJoin(async); + + AssertSql( + """ +@p0='100' +@p='0' + +DELETE FROM "Order Details" AS o +WHERE EXISTS ( + SELECT 1 + FROM "Order Details" AS o0 + LEFT JOIN ( + SELECT o2."OrderID" + FROM "Orders" AS o2 + WHERE o2."OrderID" < 10300 + ORDER BY o2."OrderID" NULLS FIRST + LIMIT @p0 OFFSET @p + ) AS o1 ON o0."OrderID" = o1."OrderID" + WHERE o0."OrderID" < 10276 AND o0."OrderID" = o."OrderID" AND o0."ProductID" = o."ProductID") +"""); + } + + public override async Task Delete_with_LeftJoin_via_flattened_GroupJoin(bool async) + { + await base.Delete_with_LeftJoin_via_flattened_GroupJoin(async); AssertSql( """ @@ -601,10 +625,12 @@ public override async Task Update_Where_set_constant_TagWith(bool async) AssertExecuteUpdateSql( """ +@p='Updated' + -- MyUpdate UPDATE "Customers" AS c -SET "ContactName" = 'Updated' +SET "ContactName" = @p WHERE c."CustomerID" LIKE 'F%' """); } @@ -613,6 +639,20 @@ public override async Task Update_Where_set_constant(bool async) { await base.Update_Where_set_constant(async); + AssertExecuteUpdateSql( + """ +@p='Updated' + +UPDATE "Customers" AS c +SET "ContactName" = @p +WHERE c."CustomerID" LIKE 'F%' +"""); + } + + public override async Task Update_Where_set_constant_via_lambda(bool async) + { + await base.Update_Where_set_constant_via_lambda(async); + AssertExecuteUpdateSql( """ UPDATE "Customers" AS c @@ -627,10 +667,11 @@ public override async Task Update_Where_parameter_set_constant(bool async) AssertExecuteUpdateSql( """ +@p='Updated' @customer='ALFKI' UPDATE "Customers" AS c -SET "ContactName" = 'Updated' +SET "ContactName" = @p WHERE c."CustomerID" = @customer """, // @@ -649,8 +690,10 @@ WHERE FALSE """, // """ +@p='Updated' + UPDATE "Customers" AS c -SET "ContactName" = 'Updated' +SET "ContactName" = @p WHERE FALSE """); } @@ -661,10 +704,10 @@ public override async Task Update_Where_set_parameter(bool async) AssertExecuteUpdateSql( """ -@value='Abc' +@p='Abc' UPDATE "Customers" AS c -SET "ContactName" = @value +SET "ContactName" = @p WHERE c."CustomerID" LIKE 'F%' """); } @@ -689,8 +732,10 @@ public override async Task Update_Where_set_parameter_from_inline_list(bool asyn AssertExecuteUpdateSql( """ +@p='Abc' + UPDATE "Customers" AS c -SET "ContactName" = 'Abc' +SET "ContactName" = @p WHERE c."CustomerID" LIKE 'F%' """); } @@ -701,10 +746,10 @@ public override async Task Update_Where_set_parameter_from_multilevel_property_a AssertExecuteUpdateSql( """ -@container_Containee_Property='Abc' +@p='Abc' UPDATE "Customers" AS c -SET "ContactName" = @container_Containee_Property +SET "ContactName" = @p WHERE c."CustomerID" LIKE 'F%' """); } @@ -727,8 +772,10 @@ public override async Task Update_Where_OrderBy_set_constant(bool async) AssertExecuteUpdateSql( """ +@p='Updated' + UPDATE "Customers" AS c0 -SET "ContactName" = 'Updated' +SET "ContactName" = @p FROM ( SELECT c."CustomerID" FROM "Customers" AS c @@ -744,10 +791,11 @@ public override async Task Update_Where_OrderBy_Skip_set_constant(bool async) AssertExecuteUpdateSql( """ +@p0='Updated' @p='4' UPDATE "Customers" AS c0 -SET "ContactName" = 'Updated' +SET "ContactName" = @p0 FROM ( SELECT c."CustomerID" FROM "Customers" AS c @@ -765,10 +813,11 @@ public override async Task Update_Where_OrderBy_Take_set_constant(bool async) AssertExecuteUpdateSql( """ +@p0='Updated' @p='4' UPDATE "Customers" AS c0 -SET "ContactName" = 'Updated' +SET "ContactName" = @p0 FROM ( SELECT c."CustomerID" FROM "Customers" AS c @@ -786,11 +835,12 @@ public override async Task Update_Where_OrderBy_Skip_Take_set_constant(bool asyn AssertExecuteUpdateSql( """ +@p1='Updated' @p0='4' @p='2' UPDATE "Customers" AS c0 -SET "ContactName" = 'Updated' +SET "ContactName" = @p1 FROM ( SELECT c."CustomerID" FROM "Customers" AS c @@ -808,11 +858,12 @@ public override async Task Update_Where_OrderBy_Skip_Take_Skip_Take_set_constant AssertExecuteUpdateSql( """ +@p3='Updated' @p0='6' @p='2' UPDATE "Customers" AS c1 -SET "ContactName" = 'Updated' +SET "ContactName" = @p3 FROM ( SELECT c0."CustomerID" FROM ( @@ -835,8 +886,10 @@ public override async Task Update_Where_GroupBy_aggregate_set_constant(bool asyn AssertExecuteUpdateSql( """ +@p='Updated' + UPDATE "Customers" AS c -SET "ContactName" = 'Updated' +SET "ContactName" = @p WHERE c."CustomerID" = ( SELECT o."CustomerID" FROM "Orders" AS o @@ -852,8 +905,10 @@ public override async Task Update_Where_GroupBy_First_set_constant(bool async) AssertExecuteUpdateSql( """ +@p='Updated' + UPDATE "Customers" AS c -SET "ContactName" = 'Updated' +SET "ContactName" = @p WHERE c."CustomerID" = ( SELECT ( SELECT o0."CustomerID" @@ -880,8 +935,10 @@ public override async Task Update_Where_GroupBy_First_set_constant_3(bool async) AssertExecuteUpdateSql( """ +@p='Updated' + UPDATE "Customers" AS c -SET "ContactName" = 'Updated' +SET "ContactName" = @p WHERE c."CustomerID" IN ( SELECT ( SELECT c0."CustomerID" @@ -902,8 +959,10 @@ public override async Task Update_Where_Distinct_set_constant(bool async) AssertExecuteUpdateSql( """ +@p='Updated' + UPDATE "Customers" AS c0 -SET "ContactName" = 'Updated' +SET "ContactName" = @p FROM ( SELECT DISTINCT c."CustomerID", c."Address", c."City", c."CompanyName", c."ContactName", c."ContactTitle", c."Country", c."Fax", c."Phone", c."PostalCode", c."Region" FROM "Customers" AS c @@ -937,8 +996,10 @@ public override async Task Update_Where_using_navigation_2_set_constant(bool asy AssertExecuteUpdateSql( """ +@p='1' + UPDATE "Order Details" AS o -SET "Quantity" = 1::smallint +SET "Quantity" = @p::smallint FROM "Orders" AS o0 LEFT JOIN "Customers" AS c ON o0."CustomerID" = c."CustomerID" WHERE o."OrderID" = o0."OrderID" AND c."City" = 'Seattle' @@ -1002,8 +1063,10 @@ public override async Task Update_Where_set_constant_using_ef_property(bool asyn AssertExecuteUpdateSql( """ +@p='Updated' + UPDATE "Customers" AS c -SET "ContactName" = 'Updated' +SET "ContactName" = @p WHERE c."CustomerID" LIKE 'F%' """); } @@ -1027,13 +1090,6 @@ public override async Task Update_without_property_to_set_throws(bool async) AssertExecuteUpdateSql(); } - public override async Task Update_with_invalid_lambda_throws(bool async) - { - await base.Update_with_invalid_lambda_throws(async); - - AssertExecuteUpdateSql(); - } - public override async Task Update_Where_multiple_set(bool async) { await base.Update_Where_multiple_set(async); @@ -1041,10 +1097,11 @@ public override async Task Update_Where_multiple_set(bool async) AssertExecuteUpdateSql( """ @value='Abc' +@p='Seattle' UPDATE "Customers" AS c -SET "City" = 'Seattle', - "ContactName" = @value +SET "ContactName" = @value, + "City" = @p WHERE c."CustomerID" LIKE 'F%' """); } @@ -1076,8 +1133,10 @@ public override async Task Update_Union_set_constant(bool async) AssertExecuteUpdateSql( """ +@p='Updated' + UPDATE "Customers" AS c1 -SET "ContactName" = 'Updated' +SET "ContactName" = @p FROM ( SELECT c."CustomerID", c."Address", c."City", c."CompanyName", c."ContactName", c."ContactTitle", c."Country", c."Fax", c."Phone", c."PostalCode", c."Region" FROM "Customers" AS c @@ -1097,8 +1156,10 @@ public override async Task Update_Concat_set_constant(bool async) AssertExecuteUpdateSql( """ +@p='Updated' + UPDATE "Customers" AS c1 -SET "ContactName" = 'Updated' +SET "ContactName" = @p FROM ( SELECT c."CustomerID" FROM "Customers" AS c @@ -1110,6 +1171,7 @@ WHERE c0."CustomerID" LIKE 'A%' ) AS u WHERE c1."CustomerID" = u."CustomerID" """); + } public override async Task Update_Except_set_constant(bool async) @@ -1118,8 +1180,10 @@ public override async Task Update_Except_set_constant(bool async) AssertExecuteUpdateSql( """ +@p='Updated' + UPDATE "Customers" AS c1 -SET "ContactName" = 'Updated' +SET "ContactName" = @p FROM ( SELECT c."CustomerID", c."Address", c."City", c."CompanyName", c."ContactName", c."ContactTitle", c."Country", c."Fax", c."Phone", c."PostalCode", c."Region" FROM "Customers" AS c @@ -1139,8 +1203,10 @@ public override async Task Update_Intersect_set_constant(bool async) AssertExecuteUpdateSql( """ +@p='Updated' + UPDATE "Customers" AS c1 -SET "ContactName" = 'Updated' +SET "ContactName" = @p FROM ( SELECT c."CustomerID", c."Address", c."City", c."CompanyName", c."ContactName", c."ContactTitle", c."Country", c."Fax", c."Phone", c."PostalCode", c."Region" FROM "Customers" AS c @@ -1160,8 +1226,10 @@ public override async Task Update_with_join_set_constant(bool async) AssertExecuteUpdateSql( """ +@p='Updated' + UPDATE "Customers" AS c -SET "ContactName" = 'Updated' +SET "ContactName" = @p FROM ( SELECT o."CustomerID" FROM "Orders" AS o @@ -1171,14 +1239,40 @@ WHERE o."OrderID" < 10300 """); } - public override async Task Update_with_left_join_set_constant(bool async) + public override async Task Update_with_LeftJoin(bool async) { - await base.Update_with_left_join_set_constant(async); + await base.Update_with_LeftJoin(async); AssertExecuteUpdateSql( """ +@p='Updated' + UPDATE "Customers" AS c0 -SET "ContactName" = 'Updated' +SET "ContactName" = @p +FROM ( + SELECT c."CustomerID" + FROM "Customers" AS c + LEFT JOIN ( + SELECT o."CustomerID" + FROM "Orders" AS o + WHERE o."OrderID" < 10300 + ) AS o0 ON c."CustomerID" = o0."CustomerID" + WHERE c."CustomerID" LIKE 'F%' +) AS s +WHERE c0."CustomerID" = s."CustomerID" +"""); + } + + public override async Task Update_with_LeftJoin_via_flattened_GroupJoin(bool async) + { + await base.Update_with_LeftJoin_via_flattened_GroupJoin(async); + + AssertExecuteUpdateSql( + """ +@p='Updated' + +UPDATE "Customers" AS c0 +SET "ContactName" = @p FROM ( SELECT c."CustomerID" FROM "Customers" AS c @@ -1199,8 +1293,10 @@ public override async Task Update_with_cross_join_set_constant(bool async) AssertExecuteUpdateSql( """ +@p='Updated' + UPDATE "Customers" AS c -SET "ContactName" = 'Updated' +SET "ContactName" = @p FROM ( SELECT 1 FROM "Orders" AS o @@ -1216,8 +1312,10 @@ public override async Task Update_with_cross_apply_set_constant(bool async) AssertExecuteUpdateSql( """ +@p='Updated' + UPDATE "Customers" AS c0 -SET "ContactName" = 'Updated' +SET "ContactName" = @p FROM ( SELECT c."CustomerID" FROM "Customers" AS c @@ -1238,8 +1336,10 @@ public override async Task Update_with_outer_apply_set_constant(bool async) AssertExecuteUpdateSql( """ +@p='Updated' + UPDATE "Customers" AS c0 -SET "ContactName" = 'Updated' +SET "ContactName" = @p FROM ( SELECT c."CustomerID" FROM "Customers" AS c @@ -1261,8 +1361,10 @@ public override async Task Update_with_cross_join_left_join_set_constant(bool as AssertExecuteUpdateSql( """ +@p='Updated' + UPDATE "Customers" AS c2 -SET "ContactName" = 'Updated' +SET "ContactName" = @p FROM ( SELECT c."CustomerID" FROM "Customers" AS c @@ -1289,8 +1391,10 @@ public override async Task Update_with_cross_join_cross_apply_set_constant(bool AssertExecuteUpdateSql( """ +@p='Updated' + UPDATE "Customers" AS c2 -SET "ContactName" = 'Updated' +SET "ContactName" = @p FROM ( SELECT c."CustomerID" FROM "Customers" AS c @@ -1317,8 +1421,10 @@ public override async Task Update_with_cross_join_outer_apply_set_constant(bool AssertExecuteUpdateSql( """ +@p='Updated' + UPDATE "Customers" AS c2 -SET "ContactName" = 'Updated' +SET "ContactName" = @p FROM ( SELECT c."CustomerID" FROM "Customers" AS c @@ -1434,8 +1540,10 @@ await AssertUpdate( AssertExecuteUpdateSql( """ +@p='1' + UPDATE "Order Details" AS o -SET "Quantity" = 1::smallint +SET "Quantity" = @p::smallint FROM "Products" AS p, "Orders" AS o0 WHERE o."OrderID" = o0."OrderID" AND o."ProductID" = p."ProductID" AND p."Discontinued" AND o0."OrderDate" > TIMESTAMP '1990-01-01T00:00:00' diff --git a/test/EFCore.PG.FunctionalTests/BulkUpdates/TPCFiltersInheritanceBulkUpdatesNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/BulkUpdates/TPCFiltersInheritanceBulkUpdatesNpgsqlTest.cs index 0d4966c9b..f428006ef 100644 --- a/test/EFCore.PG.FunctionalTests/BulkUpdates/TPCFiltersInheritanceBulkUpdatesNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/BulkUpdates/TPCFiltersInheritanceBulkUpdatesNpgsqlTest.cs @@ -122,8 +122,10 @@ public override async Task Update_base_property_on_derived_type(bool async) AssertExecuteUpdateSql( """ +@p='SomeOtherKiwi' + UPDATE "Kiwi" AS k -SET "Name" = 'SomeOtherKiwi' +SET "Name" = @p WHERE k."CountryId" = 1 """); } @@ -134,8 +136,10 @@ public override async Task Update_derived_property_on_derived_type(bool async) AssertExecuteUpdateSql( """ +@p='0' (DbType = Int16) + UPDATE "Kiwi" AS k -SET "FoundOn" = 0 +SET "FoundOn" = @p WHERE k."CountryId" = 1 """); } @@ -146,8 +150,10 @@ public override async Task Update_where_using_hierarchy(bool async) AssertExecuteUpdateSql( """ +@p='Monovia' + UPDATE "Countries" AS c -SET "Name" = 'Monovia' +SET "Name" = @p WHERE ( SELECT count(*)::int FROM ( @@ -167,9 +173,12 @@ public override async Task Update_base_and_derived_types(bool async) AssertExecuteUpdateSql( """ +@p='Kiwi' +@p0='0' (DbType = Int16) + UPDATE "Kiwi" AS k -SET "FoundOn" = 0, - "Name" = 'Kiwi' +SET "Name" = @p, + "FoundOn" = @p0 WHERE k."CountryId" = 1 """); } @@ -180,8 +189,10 @@ public override async Task Update_where_using_hierarchy_derived(bool async) AssertExecuteUpdateSql( """ +@p='Monovia' + UPDATE "Countries" AS c -SET "Name" = 'Monovia' +SET "Name" = @p WHERE ( SELECT count(*)::int FROM ( diff --git a/test/EFCore.PG.FunctionalTests/BulkUpdates/TPCInheritanceBulkUpdatesNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/BulkUpdates/TPCInheritanceBulkUpdatesNpgsqlTest.cs index 1e12c4bd5..7fec7bb60 100644 --- a/test/EFCore.PG.FunctionalTests/BulkUpdates/TPCInheritanceBulkUpdatesNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/BulkUpdates/TPCInheritanceBulkUpdatesNpgsqlTest.cs @@ -122,8 +122,10 @@ public override async Task Update_base_property_on_derived_type(bool async) AssertExecuteUpdateSql( """ +@p='SomeOtherKiwi' + UPDATE "Kiwi" AS k -SET "Name" = 'SomeOtherKiwi' +SET "Name" = @p """); } @@ -133,8 +135,10 @@ public override async Task Update_derived_property_on_derived_type(bool async) AssertExecuteUpdateSql( """ +@p='0' (DbType = Int16) + UPDATE "Kiwi" AS k -SET "FoundOn" = 0 +SET "FoundOn" = @p """); } @@ -144,8 +148,10 @@ public override async Task Update_where_using_hierarchy(bool async) AssertExecuteUpdateSql( """ +@p='Monovia' + UPDATE "Countries" AS c -SET "Name" = 'Monovia' +SET "Name" = @p WHERE ( SELECT count(*)::int FROM ( @@ -165,9 +171,12 @@ public override async Task Update_base_and_derived_types(bool async) AssertExecuteUpdateSql( """ +@p='Kiwi' +@p0='0' (DbType = Int16) + UPDATE "Kiwi" AS k -SET "FoundOn" = 0, - "Name" = 'Kiwi' +SET "Name" = @p, + "FoundOn" = @p0 """); } @@ -177,8 +186,10 @@ public override async Task Update_where_using_hierarchy_derived(bool async) AssertExecuteUpdateSql( """ +@p='Monovia' + UPDATE "Countries" AS c -SET "Name" = 'Monovia' +SET "Name" = @p WHERE ( SELECT count(*)::int FROM ( @@ -202,8 +213,10 @@ public override async Task Update_with_interface_in_property_expression(bool asy AssertExecuteUpdateSql( """ +@p='0' + UPDATE "Coke" AS c -SET "SugarGrams" = 0 +SET "SugarGrams" = @p """); } @@ -213,8 +226,10 @@ public override async Task Update_with_interface_in_EF_Property_in_property_expr AssertExecuteUpdateSql( """ +@p='0' + UPDATE "Coke" AS c -SET "SugarGrams" = 0 +SET "SugarGrams" = @p """); } diff --git a/test/EFCore.PG.FunctionalTests/BulkUpdates/TPHFiltersInheritanceBulkUpdatesNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/BulkUpdates/TPHFiltersInheritanceBulkUpdatesNpgsqlTest.cs index 5f6dbb246..2e7253872 100644 --- a/test/EFCore.PG.FunctionalTests/BulkUpdates/TPHFiltersInheritanceBulkUpdatesNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/BulkUpdates/TPHFiltersInheritanceBulkUpdatesNpgsqlTest.cs @@ -104,8 +104,10 @@ public override async Task Update_base_type(bool async) AssertExecuteUpdateSql( """ +@p='Animal' + UPDATE "Animals" AS a -SET "Name" = 'Animal' +SET "Name" = @p WHERE a."CountryId" = 1 AND a."Name" = 'Great spotted kiwi' """); } @@ -116,8 +118,10 @@ public override async Task Update_base_type_with_OfType(bool async) AssertExecuteUpdateSql( """ +@p='NewBird' + UPDATE "Animals" AS a -SET "Name" = 'NewBird' +SET "Name" = @p WHERE a."CountryId" = 1 AND a."Discriminator" = 'Kiwi' """); } @@ -155,8 +159,10 @@ public override async Task Update_base_property_on_derived_type(bool async) AssertExecuteUpdateSql( """ +@p='SomeOtherKiwi' + UPDATE "Animals" AS a -SET "Name" = 'SomeOtherKiwi' +SET "Name" = @p WHERE a."Discriminator" = 'Kiwi' AND a."CountryId" = 1 """); } @@ -167,8 +173,10 @@ public override async Task Update_derived_property_on_derived_type(bool async) AssertExecuteUpdateSql( """ +@p='0' (DbType = Int16) + UPDATE "Animals" AS a -SET "FoundOn" = 0 +SET "FoundOn" = @p WHERE a."Discriminator" = 'Kiwi' AND a."CountryId" = 1 """); } @@ -179,9 +187,12 @@ public override async Task Update_base_and_derived_types(bool async) AssertExecuteUpdateSql( """ +@p='Kiwi' +@p0='0' (DbType = Int16) + UPDATE "Animals" AS a -SET "FoundOn" = 0, - "Name" = 'Kiwi' +SET "Name" = @p, + "FoundOn" = @p0 WHERE a."Discriminator" = 'Kiwi' AND a."CountryId" = 1 """); } @@ -192,8 +203,10 @@ public override async Task Update_where_using_hierarchy(bool async) AssertExecuteUpdateSql( """ +@p='Monovia' + UPDATE "Countries" AS c -SET "Name" = 'Monovia' +SET "Name" = @p WHERE ( SELECT count(*)::int FROM "Animals" AS a @@ -207,8 +220,10 @@ public override async Task Update_where_using_hierarchy_derived(bool async) AssertExecuteUpdateSql( """ +@p='Monovia' + UPDATE "Countries" AS c -SET "Name" = 'Monovia' +SET "Name" = @p WHERE ( SELECT count(*)::int FROM "Animals" AS a diff --git a/test/EFCore.PG.FunctionalTests/BulkUpdates/TPHInheritanceBulkUpdatesNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/BulkUpdates/TPHInheritanceBulkUpdatesNpgsqlTest.cs index f1f7a9250..568df71db 100644 --- a/test/EFCore.PG.FunctionalTests/BulkUpdates/TPHInheritanceBulkUpdatesNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/BulkUpdates/TPHInheritanceBulkUpdatesNpgsqlTest.cs @@ -68,8 +68,10 @@ public override async Task Update_base_type(bool async) AssertExecuteUpdateSql( """ +@p='Animal' + UPDATE "Animals" AS a -SET "Name" = 'Animal' +SET "Name" = @p WHERE a."Name" = 'Great spotted kiwi' """); } @@ -80,8 +82,10 @@ public override async Task Update_base_type_with_OfType(bool async) AssertExecuteUpdateSql( """ +@p='NewBird' + UPDATE "Animals" AS a -SET "Name" = 'NewBird' +SET "Name" = @p WHERE a."Discriminator" = 'Kiwi' """); } @@ -165,8 +169,10 @@ public override async Task Update_base_property_on_derived_type(bool async) AssertExecuteUpdateSql( """ +@p='SomeOtherKiwi' + UPDATE "Animals" AS a -SET "Name" = 'SomeOtherKiwi' +SET "Name" = @p WHERE a."Discriminator" = 'Kiwi' """); } @@ -177,8 +183,10 @@ public override async Task Update_derived_property_on_derived_type(bool async) AssertExecuteUpdateSql( """ +@p='0' (DbType = Int16) + UPDATE "Animals" AS a -SET "FoundOn" = 0 +SET "FoundOn" = @p WHERE a."Discriminator" = 'Kiwi' """); } @@ -189,9 +197,12 @@ public override async Task Update_base_and_derived_types(bool async) AssertExecuteUpdateSql( """ +@p='Kiwi' +@p0='0' (DbType = Int16) + UPDATE "Animals" AS a -SET "FoundOn" = 0, - "Name" = 'Kiwi' +SET "Name" = @p, + "FoundOn" = @p0 WHERE a."Discriminator" = 'Kiwi' """); } @@ -202,8 +213,10 @@ public override async Task Update_where_using_hierarchy(bool async) AssertExecuteUpdateSql( """ +@p='Monovia' + UPDATE "Countries" AS c -SET "Name" = 'Monovia' +SET "Name" = @p WHERE ( SELECT count(*)::int FROM "Animals" AS a @@ -217,8 +230,10 @@ public override async Task Update_where_using_hierarchy_derived(bool async) AssertExecuteUpdateSql( """ +@p='Monovia' + UPDATE "Countries" AS c -SET "Name" = 'Monovia' +SET "Name" = @p WHERE ( SELECT count(*)::int FROM "Animals" AS a @@ -239,8 +254,10 @@ public override async Task Update_with_interface_in_property_expression(bool asy AssertExecuteUpdateSql( """ +@p='0' + UPDATE "Drinks" AS d -SET "SugarGrams" = 0 +SET "SugarGrams" = @p WHERE d."Discriminator" = 1 """); } @@ -251,8 +268,10 @@ public override async Task Update_with_interface_in_EF_Property_in_property_expr AssertExecuteUpdateSql( """ +@p='0' + UPDATE "Drinks" AS d -SET "SugarGrams" = 0 +SET "SugarGrams" = @p WHERE d."Discriminator" = 1 """); } diff --git a/test/EFCore.PG.FunctionalTests/BulkUpdates/TPTFiltersInheritanceBulkUpdatesNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/BulkUpdates/TPTFiltersInheritanceBulkUpdatesNpgsqlTest.cs index 32db8953f..7fba34bc2 100644 --- a/test/EFCore.PG.FunctionalTests/BulkUpdates/TPTFiltersInheritanceBulkUpdatesNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/BulkUpdates/TPTFiltersInheritanceBulkUpdatesNpgsqlTest.cs @@ -76,8 +76,10 @@ public override async Task Update_base_type(bool async) // TODO: This over-complex SQL would get pruned after https://github.com/dotnet/efcore/issues/31083 AssertExecuteUpdateSql( """ +@p='Animal' + UPDATE "Animals" AS a0 -SET "Name" = 'Animal' +SET "Name" = @p FROM ( SELECT a."Id" FROM "Animals" AS a @@ -94,8 +96,10 @@ public override async Task Update_base_type_with_OfType(bool async) // TODO: This over-complex SQL would get pruned after https://github.com/dotnet/efcore/issues/31083 AssertExecuteUpdateSql( """ +@p='NewBird' + UPDATE "Animals" AS a0 -SET "Name" = 'NewBird' +SET "Name" = @p FROM "Birds" AS b, "Kiwi" AS k0, ( @@ -135,8 +139,10 @@ public override async Task Update_base_property_on_derived_type(bool async) AssertExecuteUpdateSql( """ +@p='SomeOtherKiwi' + UPDATE "Animals" AS a -SET "Name" = 'SomeOtherKiwi' +SET "Name" = @p FROM "Birds" AS b, "Kiwi" AS k WHERE a."Id" = k."Id" AND a."Id" = b."Id" AND a."CountryId" = 1 @@ -149,8 +155,10 @@ public override async Task Update_derived_property_on_derived_type(bool async) AssertExecuteUpdateSql( """ +@p='0' (DbType = Int16) + UPDATE "Kiwi" AS k -SET "FoundOn" = 0 +SET "FoundOn" = @p FROM "Animals" AS a INNER JOIN "Birds" AS b ON a."Id" = b."Id" WHERE a."Id" = k."Id" AND a."CountryId" = 1 @@ -170,8 +178,10 @@ public override async Task Update_where_using_hierarchy(bool async) AssertExecuteUpdateSql( """ +@p='Monovia' + UPDATE "Countries" AS c -SET "Name" = 'Monovia' +SET "Name" = @p WHERE ( SELECT count(*)::int FROM "Animals" AS a @@ -185,8 +195,10 @@ public override async Task Update_where_using_hierarchy_derived(bool async) AssertExecuteUpdateSql( """ +@p='Monovia' + UPDATE "Countries" AS c -SET "Name" = 'Monovia' +SET "Name" = @p WHERE ( SELECT count(*)::int FROM "Animals" AS a diff --git a/test/EFCore.PG.FunctionalTests/BulkUpdates/TPTInheritanceBulkUpdatesNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/BulkUpdates/TPTInheritanceBulkUpdatesNpgsqlTest.cs index 39637fe56..657179ed7 100644 --- a/test/EFCore.PG.FunctionalTests/BulkUpdates/TPTInheritanceBulkUpdatesNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/BulkUpdates/TPTInheritanceBulkUpdatesNpgsqlTest.cs @@ -61,8 +61,10 @@ public override async Task Update_base_type(bool async) // TODO: This over-complex SQL would get pruned after https://github.com/dotnet/efcore/issues/31083 AssertExecuteUpdateSql( """ +@p='Animal' + UPDATE "Animals" AS a0 -SET "Name" = 'Animal' +SET "Name" = @p FROM ( SELECT a."Id" FROM "Animals" AS a @@ -79,8 +81,10 @@ public override async Task Update_base_type_with_OfType(bool async) // TODO: This over-complex SQL would get pruned after https://github.com/dotnet/efcore/issues/31083 AssertExecuteUpdateSql( """ +@p='NewBird' + UPDATE "Animals" AS a0 -SET "Name" = 'NewBird' +SET "Name" = @p FROM "Birds" AS b, "Kiwi" AS k0, ( @@ -120,8 +124,10 @@ public override async Task Update_base_property_on_derived_type(bool async) AssertExecuteUpdateSql( """ +@p='SomeOtherKiwi' + UPDATE "Animals" AS a -SET "Name" = 'SomeOtherKiwi' +SET "Name" = @p FROM "Birds" AS b, "Kiwi" AS k WHERE a."Id" = k."Id" AND a."Id" = b."Id" @@ -134,8 +140,10 @@ public override async Task Update_derived_property_on_derived_type(bool async) AssertExecuteUpdateSql( """ +@p='0' (DbType = Int16) + UPDATE "Kiwi" AS k -SET "FoundOn" = 0 +SET "FoundOn" = @p FROM "Animals" AS a INNER JOIN "Birds" AS b ON a."Id" = b."Id" WHERE a."Id" = k."Id" @@ -155,8 +163,10 @@ public override async Task Update_where_using_hierarchy(bool async) AssertExecuteUpdateSql( """ +@p='Monovia' + UPDATE "Countries" AS c -SET "Name" = 'Monovia' +SET "Name" = @p WHERE ( SELECT count(*)::int FROM "Animals" AS a @@ -170,8 +180,10 @@ public override async Task Update_where_using_hierarchy_derived(bool async) AssertExecuteUpdateSql( """ +@p='Monovia' + UPDATE "Countries" AS c -SET "Name" = 'Monovia' +SET "Name" = @p WHERE ( SELECT count(*)::int FROM "Animals" AS a @@ -193,8 +205,10 @@ public override async Task Update_with_interface_in_property_expression(bool asy AssertExecuteUpdateSql( """ +@p='0' + UPDATE "Coke" AS c -SET "SugarGrams" = 0 +SET "SugarGrams" = @p FROM "Drinks" AS d WHERE d."Id" = c."Id" """); @@ -206,8 +220,10 @@ public override async Task Update_with_interface_in_EF_Property_in_property_expr AssertExecuteUpdateSql( """ +@p='0' + UPDATE "Coke" AS c -SET "SugarGrams" = 0 +SET "SugarGrams" = @p FROM "Drinks" AS d WHERE d."Id" = c."Id" """); diff --git a/test/EFCore.PG.FunctionalTests/Query/GearsOfWarQueryNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/GearsOfWarQueryNpgsqlTest.cs index c0a4d4b85..18bfe6e09 100644 --- a/test/EFCore.PG.FunctionalTests/Query/GearsOfWarQueryNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/GearsOfWarQueryNpgsqlTest.cs @@ -15,44 +15,6 @@ public GearsOfWarQueryNpgsqlTest(GearsOfWarQueryNpgsqlFixture fixture, ITestOutp #region Byte array - public override async Task Byte_array_contains_literal(bool async) - { - await base.Byte_array_contains_literal(async); - - AssertSql( - """ -SELECT s."Id", s."Banner", s."Banner5", s."InternalNumber", s."Name" -FROM "Squads" AS s -WHERE position(BYTEA E'\\x01' IN s."Banner") > 0 -"""); - } - - public override async Task Byte_array_contains_parameter(bool async) - { - await base.Byte_array_contains_parameter(async); - - AssertSql( - """ -@someByte='1' (DbType = Int16) - -SELECT s."Id", s."Banner", s."Banner5", s."InternalNumber", s."Name" -FROM "Squads" AS s -WHERE position(set_byte(BYTEA E'\\x00', 0, @someByte) IN s."Banner") > 0 -"""); - } - - public override async Task Byte_array_filter_by_length_literal(bool async) - { - await base.Byte_array_filter_by_length_literal(async); - - AssertSql( - """ -SELECT s."Id", s."Banner", s."Banner5", s."InternalNumber", s."Name" -FROM "Squads" AS s -WHERE length(s."Banner") = 2 -"""); - } - public override async Task Byte_array_filter_by_length_literal_does_not_cast_on_varbinary_n(bool async) { await base.Byte_array_filter_by_length_literal_does_not_cast_on_varbinary_n(async); @@ -65,34 +27,6 @@ WHERE length(s."Banner5") = 5 """); } - public override async Task Byte_array_filter_by_length_parameter(bool async) - { - await base.Byte_array_filter_by_length_parameter(async); - - AssertSql( - """ -@p='2' - -SELECT s."Id", s."Banner", s."Banner5", s."InternalNumber", s."Name" -FROM "Squads" AS s -WHERE length(s."Banner") = @p -"""); - } - - public override void Byte_array_filter_by_length_parameter_compiled() - { - base.Byte_array_filter_by_length_parameter_compiled(); - - AssertSql( - """ -@byteArrayParam='0x2A80' - -SELECT count(*)::int -FROM "Squads" AS s -WHERE length(s."Banner") = length(@byteArrayParam) -"""); - } - #endregion Byte array #region DateTimeOffset diff --git a/test/EFCore.PG.FunctionalTests/Query/NorthwindWhereQueryNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/NorthwindWhereQueryNpgsqlTest.cs index dfabc66dd..63b709e0a 100644 --- a/test/EFCore.PG.FunctionalTests/Query/NorthwindWhereQueryNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/NorthwindWhereQueryNpgsqlTest.cs @@ -13,18 +13,6 @@ public NorthwindWhereQueryNpgsqlTest(NorthwindQueryNpgsqlFixture TRUE -"""); - } - public override async Task Where_compare_constructed_equal(bool async) { // Anonymous type to constant comparison. Issue #14672. diff --git a/test/EFCore.PG.FunctionalTests/Query/PrecompiledQueryNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/PrecompiledQueryNpgsqlTest.cs index 02e901d8b..fe82007bf 100644 --- a/test/EFCore.PG.FunctionalTests/Query/PrecompiledQueryNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/PrecompiledQueryNpgsqlTest.cs @@ -1365,9 +1365,9 @@ SELECT count(*)::int """); } - public override async Task Terminating_ExecuteUpdate() + public override async Task Terminating_ExecuteUpdate_with_lambda() { - await base.Terminating_ExecuteUpdate(); + await base.Terminating_ExecuteUpdate_with_lambda(); AssertSql( """ @@ -1385,9 +1385,29 @@ SELECT count(*)::int """); } - public override async Task Terminating_ExecuteUpdateAsync() + public override async Task Terminating_ExecuteUpdate_without_lambda() { - await base.Terminating_ExecuteUpdateAsync(); + await base.Terminating_ExecuteUpdate_without_lambda(); + + AssertSql( + """ +@newValue='NewValue' + +UPDATE "Blogs" AS b +SET "Name" = @newValue +WHERE b."Id" > 8 +""", + // + """ +SELECT count(*)::int +FROM "Blogs" AS b +WHERE b."Id" = 9 AND b."Name" = 'NewValue' +"""); + } + + public override async Task Terminating_ExecuteUpdateAsync_with_lambda() + { + await base.Terminating_ExecuteUpdateAsync_with_lambda(); AssertSql( """ @@ -1405,6 +1425,26 @@ SELECT count(*)::int """); } + public override async Task Terminating_ExecuteUpdateAsync_without_lambda() + { + await base.Terminating_ExecuteUpdateAsync_without_lambda(); + + AssertSql( + """ +@newValue='NewValue' + +UPDATE "Blogs" AS b +SET "Name" = @newValue +WHERE b."Id" > 8 +""", + // + """ +SELECT count(*)::int +FROM "Blogs" AS b +WHERE b."Id" = 9 AND b."Name" = 'NewValue' +"""); + } + #endregion Reducing terminating operators #region SQL expression quotability diff --git a/test/EFCore.PG.FunctionalTests/Query/Translations/MiscellaneousTranslationsNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/Translations/MiscellaneousTranslationsNpgsqlTest.cs index 4c184750c..0db66fd03 100644 --- a/test/EFCore.PG.FunctionalTests/Query/Translations/MiscellaneousTranslationsNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/Translations/MiscellaneousTranslationsNpgsqlTest.cs @@ -114,6 +114,58 @@ WHERE length(b."ByteArray") >= 1 AND get_byte(b."ByteArray", 0)::smallint = 222 """); } + public override async Task Byte_array_Contains_with_constant(bool async) + { + await base.Byte_array_Contains_with_constant(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE position(BYTEA E'\\x01' IN b."ByteArray") > 0 +"""); + } + + public override async Task Byte_array_Contains_with_parameter(bool async) + { + await base.Byte_array_Contains_with_parameter(async); + + AssertSql( + """ +@someByte='1' (DbType = Int16) + +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE position(set_byte(BYTEA E'\\x00', 0, @someByte) IN b."ByteArray") > 0 +"""); + } + + public override async Task Byte_array_Contains_with_column(bool async) + { + await base.Byte_array_Contains_with_column(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE position(set_byte(BYTEA E'\\x00', 0, b."Byte") IN b."ByteArray") > 0 +"""); + } + + public override async Task Byte_array_SequenceEqual(bool async) + { + await base.Byte_array_SequenceEqual(async); + + AssertSql( + """ +@byteArrayParam='0xDEADBEEF' + +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE b."ByteArray" = @byteArrayParam +"""); + } + #endregion Byte array #region Random diff --git a/test/EFCore.PG.FunctionalTests/Query/Translations/Operators/ArithmeticOperatorTranslationsNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/Translations/Operators/ArithmeticOperatorTranslationsNpgsqlTest.cs new file mode 100644 index 000000000..5b63342c1 --- /dev/null +++ b/test/EFCore.PG.FunctionalTests/Query/Translations/Operators/ArithmeticOperatorTranslationsNpgsqlTest.cs @@ -0,0 +1,78 @@ +namespace Microsoft.EntityFrameworkCore.Query.Translations.Operators; + +public class ArithmeticOperatorTranslationsNpgsqlTest : ArithmeticOperatorTranslationsTestBase +{ + public ArithmeticOperatorTranslationsNpgsqlTest(BasicTypesQueryNpgsqlFixture fixture, ITestOutputHelper testOutputHelper) + : base(fixture) + { + Fixture.TestSqlLoggerFactory.Clear(); + Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); + } + + public override async Task Add(bool async) + { + await base.Add(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE b."Int" + 2 = 10 +"""); + } + + public override async Task Subtract(bool async) + { + await base.Subtract(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE b."Int" - 3 = 5 +"""); + } + + public override async Task Multiply(bool async) + { + await base.Multiply(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE b."Int" * 2 = 16 +"""); + } + + public override async Task Modulo(bool async) + { + await base.Modulo(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE b."Int" % 3 = 2 +"""); + } + + public override async Task Minus(bool async) + { + await base.Minus(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE -b."Int" = -8 +"""); + } + + [ConditionalFact] + public virtual void Check_all_tests_overridden() + => TestHelpers.AssertAllMethodsOverridden(GetType()); + + private void AssertSql(params string[] expected) + => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); +} diff --git a/test/EFCore.PG.FunctionalTests/Query/Translations/OperatorTranslationsNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/Translations/Operators/BitwiseOperatorTranslationsNpgsqlTest.cs similarity index 76% rename from test/EFCore.PG.FunctionalTests/Query/Translations/OperatorTranslationsNpgsqlTest.cs rename to test/EFCore.PG.FunctionalTests/Query/Translations/Operators/BitwiseOperatorTranslationsNpgsqlTest.cs index 070f971e6..56f7e6a5f 100644 --- a/test/EFCore.PG.FunctionalTests/Query/Translations/OperatorTranslationsNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/Translations/Operators/BitwiseOperatorTranslationsNpgsqlTest.cs @@ -1,19 +1,17 @@ -namespace Microsoft.EntityFrameworkCore.Query.Translations; +namespace Microsoft.EntityFrameworkCore.Query.Translations.Operators; -public class OperatorTranslationsNpgsqlTest : OperatorTranslationsTestBase +public class BitwiseOperatorTranslationsNpgsqlTest : BitwiseOperatorTranslationsTestBase { - public OperatorTranslationsNpgsqlTest(BasicTypesQueryNpgsqlFixture fixture, ITestOutputHelper testOutputHelper) + public BitwiseOperatorTranslationsNpgsqlTest(BasicTypesQueryNpgsqlFixture fixture, ITestOutputHelper testOutputHelper) : base(fixture) { Fixture.TestSqlLoggerFactory.Clear(); Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); } - #region Bitwise - - public override async Task Bitwise_or(bool async) + public override async Task Or(bool async) { - await base.Bitwise_or(async); + await base.Or(async); AssertSql( """ @@ -28,9 +26,9 @@ public override async Task Bitwise_or(bool async) """); } - public override async Task Bitwise_or_over_boolean(bool async) + public override async Task Or_over_boolean(bool async) { - await base.Bitwise_or_over_boolean(async); + await base.Or_over_boolean(async); AssertSql( """ @@ -45,9 +43,9 @@ public override async Task Bitwise_or_over_boolean(bool async) """); } - public override async Task Bitwise_or_multiple(bool async) + public override async Task Or_multiple(bool async) { - await base.Bitwise_or_multiple(async); + await base.Or_multiple(async); AssertSql( """ @@ -57,9 +55,9 @@ WHERE CAST(b."Int" | b."Short" AS bigint) | b."Long" = 7 """); } - public override async Task Bitwise_and(bool async) + public override async Task And(bool async) { - await base.Bitwise_and(async); + await base.And(async); AssertSql( """ @@ -74,9 +72,9 @@ SELECT b."Int" & b."Short" """); } - public override async Task Bitwise_and_over_boolean(bool async) + public override async Task And_over_boolean(bool async) { - await base.Bitwise_and_over_boolean(async); + await base.And_over_boolean(async); AssertSql( """ @@ -91,9 +89,9 @@ public override async Task Bitwise_and_over_boolean(bool async) """); } - public override async Task Bitwise_xor(bool async) + public override async Task Xor(bool async) { - await base.Bitwise_xor(async); + await base.Xor(async); AssertSql( """ @@ -108,9 +106,9 @@ SELECT b."Int" # b."Short" """); } - public override async Task Bitwise_xor_over_boolean(bool async) + public override async Task Xor_over_boolean(bool async) { - await base.Bitwise_xor_over_boolean(async); + await base.Xor_over_boolean(async); AssertSql( """ @@ -120,9 +118,9 @@ public override async Task Bitwise_xor_over_boolean(bool async) """); } - public override async Task Bitwise_complement(bool async) + public override async Task Complement(bool async) { - await base.Bitwise_complement(async); + await base.Complement(async); AssertSql( """ @@ -132,9 +130,9 @@ public override async Task Bitwise_complement(bool async) """); } - public override async Task Bitwise_and_or_over_boolean(bool async) + public override async Task And_or_over_boolean(bool async) { - await base.Bitwise_and_or_over_boolean(async); + await base.And_or_over_boolean(async); AssertSql( """ @@ -144,9 +142,9 @@ public override async Task Bitwise_and_or_over_boolean(bool async) """); } - public override async Task Bitwise_or_with_logical_or(bool async) + public override async Task Or_with_logical_or(bool async) { - await base.Bitwise_or_with_logical_or(async); + await base.Or_with_logical_or(async); AssertSql( """ @@ -156,9 +154,9 @@ public override async Task Bitwise_or_with_logical_or(bool async) """); } - public override async Task Bitwise_and_with_logical_and(bool async) + public override async Task And_with_logical_and(bool async) { - await base.Bitwise_and_with_logical_and(async); + await base.And_with_logical_and(async); AssertSql( """ @@ -168,9 +166,9 @@ public override async Task Bitwise_and_with_logical_and(bool async) """); } - public override async Task Bitwise_or_with_logical_and(bool async) + public override async Task Or_with_logical_and(bool async) { - await base.Bitwise_or_with_logical_and(async); + await base.Or_with_logical_and(async); AssertSql( """ @@ -180,9 +178,9 @@ public override async Task Bitwise_or_with_logical_and(bool async) """); } - public override async Task Bitwise_and_with_logical_or(bool async) + public override async Task And_with_logical_or(bool async) { - await base.Bitwise_and_with_logical_or(async); + await base.And_with_logical_or(async); AssertSql( """ @@ -192,7 +190,11 @@ public override async Task Bitwise_and_with_logical_or(bool async) """); } - #endregion Bitwise + public override Task Left_shift(bool async) + => AssertTranslationFailed(() => base.Left_shift(async)); + + public override Task Right_shift(bool async) + => AssertTranslationFailed(() => base.Right_shift(async)); [ConditionalFact] public virtual void Check_all_tests_overridden() diff --git a/test/EFCore.PG.FunctionalTests/Query/Translations/Operators/ComparisonOperatorTranslationsNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/Translations/Operators/ComparisonOperatorTranslationsNpgsqlTest.cs new file mode 100644 index 000000000..b92a596cd --- /dev/null +++ b/test/EFCore.PG.FunctionalTests/Query/Translations/Operators/ComparisonOperatorTranslationsNpgsqlTest.cs @@ -0,0 +1,90 @@ +namespace Microsoft.EntityFrameworkCore.Query.Translations.Operators; + +public class ComparisonOperatorTranslationsNpgsqlTest : ComparisonOperatorTranslationsTestBase +{ + public ComparisonOperatorTranslationsNpgsqlTest(BasicTypesQueryNpgsqlFixture fixture, ITestOutputHelper testOutputHelper) + : base(fixture) + { + Fixture.TestSqlLoggerFactory.Clear(); + Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); + } + + public override async Task Equal(bool async) + { + await base.Equal(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE b."Int" = 8 +"""); + } + + public override async Task NotEqual(bool async) + { + await base.NotEqual(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE b."Int" <> 8 +"""); + } + + public override async Task GreaterThan(bool async) + { + await base.GreaterThan(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE b."Int" > 8 +"""); + } + + public override async Task GreaterThanOrEqual(bool async) + { + await base.GreaterThanOrEqual(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE b."Int" >= 8 +"""); + } + + public override async Task LessThan(bool async) + { + await base.LessThan(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE b."Int" < 8 +"""); + } + + public override async Task LessThanOrEqual(bool async) + { + await base.LessThanOrEqual(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE b."Int" <= 8 +"""); + } + + [ConditionalFact] + public virtual void Check_all_tests_overridden() + => TestHelpers.AssertAllMethodsOverridden(GetType()); + + private void AssertSql(params string[] expected) + => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); +} diff --git a/test/EFCore.PG.FunctionalTests/Query/Translations/Operators/LogicalOperatorTranslationsNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/Translations/Operators/LogicalOperatorTranslationsNpgsqlTest.cs new file mode 100644 index 000000000..44d9a44d3 --- /dev/null +++ b/test/EFCore.PG.FunctionalTests/Query/Translations/Operators/LogicalOperatorTranslationsNpgsqlTest.cs @@ -0,0 +1,90 @@ +namespace Microsoft.EntityFrameworkCore.Query.Translations.Operators; + +public class LogicalOperatorTranslationsNpgsqlTest : LogicalOperatorTranslationsTestBase +{ + public LogicalOperatorTranslationsNpgsqlTest(BasicTypesQueryNpgsqlFixture fixture, ITestOutputHelper testOutputHelper) + : base(fixture) + { + Fixture.TestSqlLoggerFactory.Clear(); + Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); + } + + public override async Task And(bool async) + { + await base.And(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE b."Int" = 8 AND b."String" = 'Seattle' +"""); + } + + public override async Task And_with_bool_property(bool async) + { + await base.And_with_bool_property(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE b."Bool" AND b."String" = 'Seattle' +"""); + } + + public override async Task Or(bool async) + { + await base.Or(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE b."Int" = 999 OR b."String" = 'Seattle' +"""); + } + + public override async Task Or_with_bool_property(bool async) + { + await base.Or_with_bool_property(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE b."Bool" OR b."String" = 'Seattle' +"""); + } + + public override async Task Not(bool async) + { + await base.Not(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE b."Int" <> 999 +"""); + } + + public override async Task Not_with_bool_property(bool async) + { + await base.Not_with_bool_property(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE NOT (b."Bool") +"""); + } + + [ConditionalFact] + public virtual void Check_all_tests_overridden() + => TestHelpers.AssertAllMethodsOverridden(GetType()); + + private void AssertSql(params string[] expected) + => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); +} diff --git a/test/EFCore.PG.FunctionalTests/Query/Translations/Operators/MiscellaneousOperatorTranslationsNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/Translations/Operators/MiscellaneousOperatorTranslationsNpgsqlTest.cs new file mode 100644 index 000000000..4ef98ce76 --- /dev/null +++ b/test/EFCore.PG.FunctionalTests/Query/Translations/Operators/MiscellaneousOperatorTranslationsNpgsqlTest.cs @@ -0,0 +1,45 @@ +namespace Microsoft.EntityFrameworkCore.Query.Translations.Operators; + +public class MiscellaneousOperatorTranslationsNpgsqlTest : MiscellaneousOperatorTranslationsTestBase +{ + public MiscellaneousOperatorTranslationsNpgsqlTest(BasicTypesQueryNpgsqlFixture fixture, ITestOutputHelper testOutputHelper) + : base(fixture) + { + Fixture.TestSqlLoggerFactory.Clear(); + Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); + } + + public override async Task Conditional(bool async) + { + await base.Conditional(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE CASE + WHEN b."Int" = 8 THEN b."String" + ELSE 'Foo' +END = 'Seattle' +"""); + } + + public override async Task Coalesce(bool async) + { + await base.Coalesce(async); + + AssertSql( + """ +SELECT n."Id", n."Bool", n."Byte", n."ByteArray", n."DateOnly", n."DateTime", n."DateTimeOffset", n."Decimal", n."Double", n."Enum", n."FlagsEnum", n."Float", n."Guid", n."Int", n."Long", n."Short", n."String", n."TimeOnly", n."TimeSpan" +FROM "NullableBasicTypesEntities" AS n +WHERE COALESCE(n."String", 'Unknown') = 'Seattle' +"""); + } + + [ConditionalFact] + public virtual void Check_all_tests_overridden() + => TestHelpers.AssertAllMethodsOverridden(GetType()); + + private void AssertSql(params string[] expected) + => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); +} diff --git a/test/EFCore.PG.FunctionalTests/Query/Translations/StringTranslationsNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/Translations/StringTranslationsNpgsqlTest.cs index 77a3ccd11..4b58db5a2 100644 --- a/test/EFCore.PG.FunctionalTests/Query/Translations/StringTranslationsNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/Translations/StringTranslationsNpgsqlTest.cs @@ -90,6 +90,11 @@ public override async Task ToUpper(bool async) SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" FROM "BasicTypesEntities" AS b WHERE upper(b."String") = 'SEATTLE' +""", + // + """ +SELECT upper(b."String") +FROM "BasicTypesEntities" AS b """); } @@ -102,6 +107,11 @@ public override async Task ToLower(bool async) SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" FROM "BasicTypesEntities" AS b WHERE lower(b."String") = 'seattle' +""", + // + """ +SELECT lower(b."String") +FROM "BasicTypesEntities" AS b """); } @@ -1257,6 +1267,18 @@ WHERE concat_ws('|', b."String", @foo, '', 'bar') = 'Seattle|foo||bar' #region Concatenation + public override async Task Concat_operator(bool async) + { + await base.Concat_operator(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE b."String" || 'Boston' = 'SeattleBoston' +"""); + } + public override async Task Concat_aggregate(bool async) { await base.Concat_aggregate(async); diff --git a/test/EFCore.PG.FunctionalTests/Query/Translations/Temporal/DateOnlyTranslationsNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/Translations/Temporal/DateOnlyTranslationsNpgsqlTest.cs new file mode 100644 index 000000000..18865635d --- /dev/null +++ b/test/EFCore.PG.FunctionalTests/Query/Translations/Temporal/DateOnlyTranslationsNpgsqlTest.cs @@ -0,0 +1,212 @@ +namespace Microsoft.EntityFrameworkCore.Query.Translations.Temporal; + +public class DateOnlyTranslationsNpgsqlTest : DateOnlyTranslationsTestBase +{ + public DateOnlyTranslationsNpgsqlTest(BasicTypesQueryNpgsqlFixture fixture, ITestOutputHelper testOutputHelper) + : base(fixture) + { + Fixture.TestSqlLoggerFactory.Clear(); + Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); + } + + public override async Task Year(bool async) + { + await base.Year(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE date_part('year', b."DateOnly")::int = 1990 +"""); + } + + public override async Task Month(bool async) + { + await base.Month(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE date_part('month', b."DateOnly")::int = 11 +"""); + } + + public override async Task Day(bool async) + { + await base.Day(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE date_part('day', b."DateOnly")::int = 10 +"""); + } + + public override async Task DayOfYear(bool async) + { + await base.DayOfYear(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE date_part('doy', b."DateOnly")::int = 314 +"""); + } + + public override async Task DayOfWeek(bool async) + { + await base.DayOfWeek(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE floor(date_part('dow', b."DateOnly"))::int = 6 +"""); + } + + public override async Task AddYears(bool async) + { + await base.AddYears(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE CAST(b."DateOnly" + INTERVAL '3 years' AS date) = DATE '1993-11-10' +"""); + } + + public override async Task AddMonths(bool async) + { + await base.AddMonths(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE CAST(b."DateOnly" + INTERVAL '3 months' AS date) = DATE '1991-02-10' +"""); + } + + public override async Task AddDays(bool async) + { + await base.AddDays(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE b."DateOnly" + 3 = DATE '1990-11-13' +"""); + } + + public override async Task FromDateTime(bool async) + { + await base.FromDateTime(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE CAST(b."DateTime" AT TIME ZONE 'UTC' AS date) = DATE '1998-05-04' +"""); + } + + public override async Task FromDateTime_compared_to_property(bool async) + { + await base.FromDateTime_compared_to_property(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE CAST(b."DateTime" AT TIME ZONE 'UTC' AS date) = b."DateOnly" +"""); + } + + public override async Task FromDateTime_compared_to_constant_and_parameter(bool async) + { + await base.FromDateTime_compared_to_constant_and_parameter(async); + + AssertSql( + """ +@dateOnly='10/11/0002' (DbType = Date) + +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE CAST(b."DateTime" AT TIME ZONE 'UTC' AS date) IN (@dateOnly, DATE '1998-05-04') +"""); + } + + public override async Task ToDateTime_property_with_constant_TimeOnly(bool async) + { + await base.ToDateTime_property_with_constant_TimeOnly(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE b."DateOnly" + TIME '21:05:19.9405' = TIMESTAMP '2020-01-01T21:05:19.9405' +"""); + } + + public override async Task ToDateTime_property_with_property_TimeOnly(bool async) + { + await base.ToDateTime_property_with_property_TimeOnly(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE b."DateOnly" + b."TimeOnly" = TIMESTAMP '2020-01-01T15:30:10' +"""); + } + + public override async Task ToDateTime_constant_DateTime_with_property_TimeOnly(bool async) + { + await base.ToDateTime_constant_DateTime_with_property_TimeOnly(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE DATE '1990-11-10' + b."TimeOnly" = TIMESTAMP '1990-11-10T15:30:10' +"""); + } + + public override async Task ToDateTime_with_complex_DateTime(bool async) + { + await base.ToDateTime_with_complex_DateTime(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE CAST(b."DateOnly" + INTERVAL '1 years' AS date) + b."TimeOnly" = TIMESTAMP '2021-01-01T15:30:10' +"""); + } + + public override async Task ToDateTime_with_complex_TimeOnly(bool async) + { + await base.ToDateTime_with_complex_TimeOnly(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE b."DateOnly" + b."TimeOnly" + INTERVAL '1 hours' = TIMESTAMP '2020-01-01T16:30:10' +"""); + } + + [ConditionalFact] + public virtual void Check_all_tests_overridden() + => TestHelpers.AssertAllMethodsOverridden(GetType()); + + private void AssertSql(params string[] expected) + => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); +} diff --git a/test/EFCore.PG.FunctionalTests/Query/Translations/Temporal/DateTimeOffsetTranslationsNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/Translations/Temporal/DateTimeOffsetTranslationsNpgsqlTest.cs new file mode 100644 index 000000000..94dd8929c --- /dev/null +++ b/test/EFCore.PG.FunctionalTests/Query/Translations/Temporal/DateTimeOffsetTranslationsNpgsqlTest.cs @@ -0,0 +1,243 @@ +using Xunit.Sdk; + +namespace Microsoft.EntityFrameworkCore.Query.Translations.Temporal; + +public class DateTimeOffsetTranslationsNpgsqlTest : DateTimeOffsetTranslationsTestBase +{ + public DateTimeOffsetTranslationsNpgsqlTest(BasicTypesQueryNpgsqlFixture fixture, ITestOutputHelper testOutputHelper) + : base(fixture) + { + Fixture.TestSqlLoggerFactory.Clear(); + Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); + } + + // Not supported by design (DateTimeOffset with non-zero offset) + public override Task Now(bool async) + => Assert.ThrowsAsync(() => base.Now(async)); + + public override async Task UtcNow(bool async) + { + await base.UtcNow(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE b."DateTimeOffset" <> now() +"""); + } + + // The test compares with new DateTimeOffset().Date, which Npgsql sends as -infinity, causing a discrepancy with the client behavior + // which uses 1/1/1:0:0:0 + public override Task Date(bool async) + => Assert.ThrowsAsync(() => base.Date(async)); + + public override async Task Year(bool async) + { + await base.Year(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE date_part('year', b."DateTimeOffset" AT TIME ZONE 'UTC')::int = 1998 +"""); + } + + public override async Task Month(bool async) + { + await base.Month(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE date_part('month', b."DateTimeOffset" AT TIME ZONE 'UTC')::int = 5 +"""); + } + + public override async Task DayOfYear(bool async) + { + await base.DayOfYear(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE date_part('doy', b."DateTimeOffset" AT TIME ZONE 'UTC')::int = 124 +"""); + } + + public override async Task Day(bool async) + { + await base.Day(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE date_part('day', b."DateTimeOffset" AT TIME ZONE 'UTC')::int = 4 +"""); + } + + public override async Task Hour(bool async) + { + await base.Hour(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE date_part('hour', b."DateTimeOffset" AT TIME ZONE 'UTC')::int = 15 +"""); + } + + public override async Task Minute(bool async) + { + await base.Minute(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE date_part('minute', b."DateTimeOffset" AT TIME ZONE 'UTC')::int = 30 +"""); + } + + public override async Task Second(bool async) + { + await base.Second(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE date_part('second', b."DateTimeOffset" AT TIME ZONE 'UTC')::int = 10 +"""); + } + + // SQL translation not implemented, too annoying + public override Task Millisecond(bool async) + => AssertTranslationFailed(() => base.Millisecond(async)); + + // TODO: #3406 + public override Task Microsecond(bool async) + => AssertTranslationFailed(() => base.Microsecond(async)); + + // TODO: #3406 + public override Task Nanosecond(bool async) + => AssertTranslationFailed(() => base.Nanosecond(async)); + + public override async Task TimeOfDay(bool async) + { + await base.TimeOfDay(async); + + AssertSql( + """ +SELECT CAST(b."DateTimeOffset" AT TIME ZONE 'UTC' AS time) +FROM "BasicTypesEntities" AS b +"""); + } + + public override async Task AddYears(bool async) + { + await base.AddYears(async); + + AssertSql( + """ +SELECT b."DateTimeOffset" + INTERVAL '1 years' +FROM "BasicTypesEntities" AS b +"""); + } + + public override async Task AddMonths(bool async) + { + await base.AddMonths(async); + + AssertSql( + """ +SELECT b."DateTimeOffset" + INTERVAL '1 months' +FROM "BasicTypesEntities" AS b +"""); + } + + public override async Task AddDays(bool async) + { + await base.AddDays(async); + + AssertSql( + """ +SELECT b."DateTimeOffset" + INTERVAL '1 days' +FROM "BasicTypesEntities" AS b +"""); + } + + public override async Task AddHours(bool async) + { + await base.AddHours(async); + + AssertSql( + """ +SELECT b."DateTimeOffset" + INTERVAL '1 hours' +FROM "BasicTypesEntities" AS b +"""); + } + + public override async Task AddMinutes(bool async) + { + await base.AddMinutes(async); + + AssertSql( + """ +SELECT b."DateTimeOffset" + INTERVAL '1 mins' +FROM "BasicTypesEntities" AS b +"""); + } + + public override async Task AddSeconds(bool async) + { + await base.AddSeconds(async); + + AssertSql( + """ +SELECT b."DateTimeOffset" + INTERVAL '1 secs' +FROM "BasicTypesEntities" AS b +"""); + } + + public override async Task AddMilliseconds(bool async) + { + await base.AddMilliseconds(async); + + AssertSql( + """ +SELECT b."DateTimeOffset" +FROM "BasicTypesEntities" AS b +"""); + } + + public override Task ToUnixTimeMilliseconds(bool async) + => AssertTranslationFailed(() => base.ToUnixTimeMilliseconds(async)); + + public override Task ToUnixTimeSecond(bool async) + => AssertTranslationFailed(() => base.ToUnixTimeSecond(async)); + + public override async Task Milliseconds_parameter_and_constant(bool async) + { + await base.Milliseconds_parameter_and_constant(async); + + AssertSql( + """ +SELECT count(*)::int +FROM "BasicTypesEntities" AS b +WHERE b."DateTimeOffset" = TIMESTAMPTZ '1902-01-02T10:00:00.123456+01:30' +"""); + } + + [ConditionalFact] + public virtual void Check_all_tests_overridden() + => TestHelpers.AssertAllMethodsOverridden(GetType()); + + private void AssertSql(params string[] expected) + => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); +} diff --git a/test/EFCore.PG.FunctionalTests/Query/Translations/Temporal/DateTimeTranslationsNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/Translations/Temporal/DateTimeTranslationsNpgsqlTest.cs new file mode 100644 index 000000000..ed9e80cd0 --- /dev/null +++ b/test/EFCore.PG.FunctionalTests/Query/Translations/Temporal/DateTimeTranslationsNpgsqlTest.cs @@ -0,0 +1,260 @@ +using Microsoft.EntityFrameworkCore.TestModels.BasicTypesModel; + +namespace Microsoft.EntityFrameworkCore.Query.Translations.Temporal; + +/// +/// Note that is mapped to PG timestamp with time zone, as is the provider default; +/// this causes issues with various tests. See also , which +/// explicitly maps to timestamp without time zone. +/// +public class DateTimeTranslationsNpgsqlTest : DateTimeTranslationsTestBase +{ + public DateTimeTranslationsNpgsqlTest(BasicTypesQueryNpgsqlFixture fixture, ITestOutputHelper testOutputHelper) + : base(fixture) + { + Fixture.TestSqlLoggerFactory.Clear(); + Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); + } + + public override async Task Now(bool async) + { + await base.Now(async); + + AssertSql( + """ +@myDatetime='2015-04-10T00:00:00.0000000' + +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE now()::timestamp <> @myDatetime +"""); + } + + public override async Task UtcNow(bool async) + { + // Overriding to set Kind=Utc for timestamptz + var myDatetime = DateTime.SpecifyKind(new DateTime(2015, 4, 10), DateTimeKind.Utc); + + await AssertQuery( + async, + ss => ss.Set().Where(c => DateTime.UtcNow != myDatetime)); + + AssertSql( + """ +@myDatetime='2015-04-10T00:00:00.0000000Z' (DbType = DateTime) + +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE now() <> @myDatetime +"""); + } + + // DateTime.Today returns a Local DateTime, which can't be compared with timestamptz + // (see TemporalTranslationsNpgsqlTimestampWithoutTimeZoneTest for a working version of this test) + public override Task Today(bool async) + => Assert.ThrowsAsync(() => base.Today(async)); + + public override async Task Date(bool async) + { + // Overriding to set Kind=Utc for timestamptz + var myDatetime = DateTime.SpecifyKind(new DateTime(1998, 5, 4), DateTimeKind.Utc); + + await AssertQuery( + async, + ss => ss.Set().Where(o => o.DateTime.Date == myDatetime)); + + AssertSql( + """ +@myDatetime='1998-05-04T00:00:00.0000000Z' (DbType = DateTime) + +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE date_trunc('day', b."DateTime", 'UTC') = @myDatetime +"""); + } + + public override async Task AddYear(bool async) + { + await base.AddYear(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE date_part('year', (b."DateTime" + INTERVAL '1 years') AT TIME ZONE 'UTC')::int = 1999 +"""); + } + + public override async Task Year(bool async) + { + await base.Year(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE date_part('year', b."DateTime" AT TIME ZONE 'UTC')::int = 1998 +"""); + } + + public override async Task Month(bool async) + { + await base.Month(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE date_part('month', b."DateTime" AT TIME ZONE 'UTC')::int = 5 +"""); + } + + public override async Task DayOfYear(bool async) + { + await base.DayOfYear(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE date_part('doy', b."DateTime" AT TIME ZONE 'UTC')::int = 124 +"""); + } + + public override async Task Day(bool async) + { + await base.Day(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE date_part('day', b."DateTime" AT TIME ZONE 'UTC')::int = 4 +"""); + } + + public override async Task Hour(bool async) + { + await base.Hour(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE date_part('hour', b."DateTime" AT TIME ZONE 'UTC')::int = 15 +"""); + } + + public override async Task Minute(bool async) + { + await base.Minute(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE date_part('minute', b."DateTime" AT TIME ZONE 'UTC')::int = 30 +"""); + } + + public override async Task Second(bool async) + { + await base.Second(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE date_part('second', b."DateTime" AT TIME ZONE 'UTC')::int = 10 +"""); + } + + // SQL translation not implemented, too annoying + public override Task Millisecond(bool async) + => AssertTranslationFailed(() => base.Millisecond(async)); + + public override async Task TimeOfDay(bool async) + { + await base.TimeOfDay(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE CAST(b."DateTime" AT TIME ZONE 'UTC' AS time) = TIME '00:00:00' +"""); + } + + public override async Task subtract_and_TotalDays(bool async) + { + // Overriding to set Kind=Utc for timestamptz + var date = DateTime.SpecifyKind(new DateTime(1997, 1, 1), DateTimeKind.Utc); + + await AssertQuery( + async, + ss => ss.Set().Where(o => (o.DateTime - date).TotalDays > 365)); + + AssertSql( + """ +@date='1997-01-01T00:00:00.0000000Z' (DbType = DateTime) + +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE date_part('epoch', b."DateTime" - @date) / 86400.0 > 365.0 +"""); + } + + // DateTime.Parse() returns either a Local or Unspecified DateTime, which can't be compared with timestamptz + // (see TemporalTranslationsNpgsqlTimestampWithoutTimeZoneTest for a working version of this test) + public override Task Parse_with_constant(bool async) + => Assert.ThrowsAsync(() => base.Parse_with_constant(async)); + + // DateTime.Parse() returns either a Local or Unspecified DateTime, which can't be compared with timestamptz + // (see TemporalTranslationsNpgsqlTimestampWithoutTimeZoneTest for a working version of this test) + public override Task Parse_with_parameter(bool async) + => Assert.ThrowsAsync(() => base.Parse_with_parameter(async)); + + public override async Task New_with_constant(bool async) + { + // Overriding to set Kind=Utc for timestamptz + await AssertQuery( + async, + ss => ss.Set().Where(o => o.DateTime == new DateTime(1998, 5, 4, 15, 30, 10, DateTimeKind.Utc))); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE b."DateTime" = TIMESTAMPTZ '1998-05-04T15:30:10Z' +"""); + } + + public override async Task New_with_parameters(bool async) + { + // Overriding to set Kind=Utc for timestamptz + var year = 1998; + var month = 5; + var date = 4; + var hour = 15; + + await AssertQuery( + async, + ss => ss.Set().Where(o => o.DateTime == new DateTime(year, month, date, hour, 30, 10, DateTimeKind.Utc))); + + AssertSql( + """ +@p='1998-05-04T15:30:10.0000000Z' (DbType = DateTime) + +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE b."DateTime" = @p +"""); + } + + [ConditionalFact] + public virtual void Check_all_tests_overridden() + => TestHelpers.AssertAllMethodsOverridden(GetType()); + + private void AssertSql(params string[] expected) + => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); +} diff --git a/test/EFCore.PG.FunctionalTests/Query/Translations/Temporal/DateTimeTranslationsWithoutTimeZoneTest.cs b/test/EFCore.PG.FunctionalTests/Query/Translations/Temporal/DateTimeTranslationsWithoutTimeZoneTest.cs new file mode 100644 index 000000000..b7cc008bd --- /dev/null +++ b/test/EFCore.PG.FunctionalTests/Query/Translations/Temporal/DateTimeTranslationsWithoutTimeZoneTest.cs @@ -0,0 +1,314 @@ +using Microsoft.EntityFrameworkCore.TestModels.BasicTypesModel; + +namespace Microsoft.EntityFrameworkCore.Query.Translations.Temporal; + +/// +/// Same as , but the property is mapped to a PostgreSQL +/// timestamp without time zone, which corresponds to a with +/// . +/// +public class DateTimeTranslationsWithoutTimeZoneTest + : DateTimeTranslationsTestBase +{ + public DateTimeTranslationsWithoutTimeZoneTest( + BasicTypesQueryNpgsqlTimestampWithoutTimeZoneFixture fixture, + ITestOutputHelper testOutputHelper) + : base(fixture) + { + Fixture.TestSqlLoggerFactory.Clear(); + Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); + } + + public override async Task Now(bool async) + { + await base.Now(async); + + AssertSql( + """ +@myDatetime='2015-04-10T00:00:00.0000000' + +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE now()::timestamp <> @myDatetime +"""); + } + + public override async Task UtcNow(bool async) + { + // Overriding to set Kind=Utc for timestamptz. This test generally doesn't make much sense here. + var myDatetime = DateTime.SpecifyKind(new DateTime(2015, 4, 10), DateTimeKind.Utc); + + await AssertQuery( + async, + ss => ss.Set().Where(c => DateTime.UtcNow != myDatetime)); + + AssertSql( + """ +@myDatetime='2015-04-10T00:00:00.0000000Z' (DbType = DateTime) + +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE now() <> @myDatetime +"""); + } + + public override async Task Today(bool async) + { + await base.Today(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE b."DateTime" = date_trunc('day', now()::timestamp) +"""); + } + + public override async Task Date(bool async) + { + await base.Date(async); + + AssertSql( + """ +@myDatetime='1998-05-04T00:00:00.0000000' + +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE date_trunc('day', b."DateTime") = @myDatetime +"""); + } + + public override async Task AddYear(bool async) + { + await base.AddYear(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE date_part('year', b."DateTime" + INTERVAL '1 years')::int = 1999 +"""); + } + + public override async Task Year(bool async) + { + await base.Year(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE date_part('year', b."DateTime")::int = 1998 +"""); + } + + public override async Task Month(bool async) + { + await base.Month(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE date_part('month', b."DateTime")::int = 5 +"""); + } + + public override async Task DayOfYear(bool async) + { + await base.DayOfYear(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE date_part('doy', b."DateTime")::int = 124 +"""); + } + + public override async Task Day(bool async) + { + await base.Day(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE date_part('day', b."DateTime")::int = 4 +"""); + } + + public override async Task Hour(bool async) + { + await base.Hour(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE date_part('hour', b."DateTime")::int = 15 +"""); + } + + public override async Task Minute(bool async) + { + await base.Minute(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE date_part('minute', b."DateTime")::int = 30 +"""); + } + + public override async Task Second(bool async) + { + await base.Second(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE date_part('second', b."DateTime")::int = 10 +"""); + } + + // SQL translation not implemented, too annoying + public override Task Millisecond(bool async) + => AssertTranslationFailed(() => base.Millisecond(async)); + + public override async Task TimeOfDay(bool async) + { + await base.TimeOfDay(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE b."DateTime"::time = TIME '00:00:00' +"""); + } + + public override async Task subtract_and_TotalDays(bool async) + { + await base.subtract_and_TotalDays(async); + + AssertSql( + """ +@date='1997-01-01T00:00:00.0000000' + +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE date_part('epoch', b."DateTime" - @date) / 86400.0 > 365.0 +"""); + } + + public override async Task Parse_with_constant(bool async) + { + await base.Parse_with_constant(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE b."DateTime" = TIMESTAMP '1998-05-04T15:30:10' +"""); + } + + public override async Task Parse_with_parameter(bool async) + { + await base.Parse_with_parameter(async); + + AssertSql( + """ +@Parse='1998-05-04T15:30:10.0000000' + +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE b."DateTime" = @Parse +"""); + } + + public override async Task New_with_constant(bool async) + { + await base.New_with_constant(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE b."DateTime" = TIMESTAMP '1998-05-04T15:30:10' +"""); + } + + public override async Task New_with_parameters(bool async) + { + await base.New_with_parameters(async); + + AssertSql( + """ +@p='1998-05-04T15:30:10.0000000' + +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE b."DateTime" = @p +"""); + } + + [ConditionalFact] + public virtual void Check_all_tests_overridden() + => TestHelpers.AssertAllMethodsOverridden(GetType()); + + private void AssertSql(params string[] expected) + => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); + + public class BasicTypesQueryNpgsqlTimestampWithoutTimeZoneFixture : BasicTypesQueryNpgsqlFixture + { + private BasicTypesData? _expectedData; + + protected override string StoreName + => "BasicTypesTimestampWithoutTimeZoneTest"; + + protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext context) + { + base.OnModelCreating(modelBuilder, context); + + modelBuilder.Entity().Property(b => b.DateTime).HasColumnType("timestamp without time zone"); + modelBuilder.Entity().Property(b => b.DateTime).HasColumnType("timestamp without time zone"); + } + + protected override Task SeedAsync(BasicTypesContext context) + { + _expectedData ??= LoadAndTweakData(); + context.AddRange(_expectedData.BasicTypesEntities); + context.AddRange(_expectedData.NullableBasicTypesEntities); + return context.SaveChangesAsync(); + } + + public override ISetSource GetExpectedData() + => _expectedData ??= LoadAndTweakData(); + + private BasicTypesData LoadAndTweakData() + { + var data = (BasicTypesData)base.GetExpectedData(); + + foreach (var item in data.BasicTypesEntities) + { + // Change Kind fo all DateTimes from Utc to Unspecified, as we're mapping to 'timestamp without time zone' + item.DateTime = DateTime.SpecifyKind(item.DateTime, DateTimeKind.Unspecified); + } + + // Do the same for the nullable counterparts + foreach (var item in data.NullableBasicTypesEntities) + { + if (item.DateTime.HasValue) + { + item.DateTime = DateTime.SpecifyKind(item.DateTime.Value, DateTimeKind.Unspecified); + } + } + + return data; + } + } +} diff --git a/test/EFCore.PG.FunctionalTests/Query/Translations/Temporal/TimeOnlyTranslationsNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/Translations/Temporal/TimeOnlyTranslationsNpgsqlTest.cs new file mode 100644 index 000000000..d159e5b7e --- /dev/null +++ b/test/EFCore.PG.FunctionalTests/Query/Translations/Temporal/TimeOnlyTranslationsNpgsqlTest.cs @@ -0,0 +1,208 @@ +using Microsoft.EntityFrameworkCore.TestModels.BasicTypesModel; + +namespace Microsoft.EntityFrameworkCore.Query.Translations.Temporal; + +public class TimeOnlyTranslationsNpgsqlTest : TimeOnlyTranslationsTestBase +{ + public TimeOnlyTranslationsNpgsqlTest(BasicTypesQueryNpgsqlFixture fixture, ITestOutputHelper testOutputHelper) + : base(fixture) + { + Fixture.TestSqlLoggerFactory.Clear(); + Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); + } + + public override async Task Hour(bool async) + { + await base.Hour(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE date_part('hour', b."TimeOnly")::int = 15 +"""); + } + + public override async Task Minute(bool async) + { + await base.Minute(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE date_part('minute', b."TimeOnly")::int = 30 +"""); + } + + public override async Task Second(bool async) + { + await base.Second(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE date_part('second', b."TimeOnly")::int = 10 +"""); + } + + // Translation not yet implemented + public override Task Millisecond(bool async) + => AssertTranslationFailed(() => base.Millisecond(async)); + + // Translation not yet implemented + public override Task Microsecond(bool async) + => AssertTranslationFailed(() => base.Millisecond(async)); + + // Probably not relevant for PostgreSQL, which supports microsecond precision only + public override Task Nanosecond(bool async) + => AssertTranslationFailed(() => base.Millisecond(async)); + + public override async Task AddHours(bool async) + { + await base.AddHours(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE b."TimeOnly" + INTERVAL '3 hours' = TIME '18:30:10' +"""); + } + + public override async Task AddMinutes(bool async) + { + await base.AddMinutes(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE b."TimeOnly" + INTERVAL '3 mins' = TIME '15:33:10' +"""); + } + + public override async Task Add_TimeSpan(bool async) + { + await base.Add_TimeSpan(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE b."TimeOnly" + INTERVAL '03:00:00' = TIME '18:30:10' +"""); + } + + public override async Task IsBetween(bool async) + { + await base.IsBetween(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE b."TimeOnly" >= TIME '14:00:00' AND b."TimeOnly" < TIME '16:00:00' +"""); + } + + public override async Task Subtract(bool async) + { + await base.Subtract(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE b."TimeOnly" - TIME '03:00:00' = INTERVAL '12:30:10' +"""); + } + + public override async Task FromDateTime_compared_to_property(bool async) + { + await base.FromDateTime_compared_to_property(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE CAST(b."DateTime" AT TIME ZONE 'UTC' AS time without time zone) = b."TimeOnly" +"""); + } + + public override async Task FromDateTime_compared_to_parameter(bool async) + { + await base.FromDateTime_compared_to_parameter(async); + + AssertSql( + """ +@time='15:30' (DbType = Time) + +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE CAST(b."DateTime" AT TIME ZONE 'UTC' AS time without time zone) = @time +"""); + } + + public override async Task FromDateTime_compared_to_constant(bool async) + { + await base.FromDateTime_compared_to_constant(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE CAST(b."DateTime" AT TIME ZONE 'UTC' AS time without time zone) = TIME '15:30:10' +"""); + } + + public override async Task FromTimeSpan_compared_to_property(bool async) + { + await base.FromTimeSpan_compared_to_property(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE b."TimeSpan"::time without time zone < b."TimeOnly" +"""); + } + + public override async Task FromTimeSpan_compared_to_parameter(bool async) + { + await base.FromTimeSpan_compared_to_parameter(async); + + AssertSql( + """ +@time='01:02' (DbType = Time) + +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE b."TimeSpan"::time without time zone = @time +"""); + } + + public override async Task Order_by_FromTimeSpan(bool async) + { + // TODO: Base implementation is non-deterministic, remove this override once that's fixed on the EF side. + await AssertQuery( + async, + ss => ss.Set().OrderBy(x => TimeOnly.FromTimeSpan(x.TimeSpan)).ThenBy(x => x.Id), + assertOrder: true); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +ORDER BY b."TimeSpan"::time without time zone NULLS FIRST, b."Id" NULLS FIRST +"""); + } + + [ConditionalFact] + public virtual void Check_all_tests_overridden() + => TestHelpers.AssertAllMethodsOverridden(GetType()); + + private void AssertSql(params string[] expected) + => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); +} diff --git a/test/EFCore.PG.FunctionalTests/Query/Translations/Temporal/TimeSpanTranslationsNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/Translations/Temporal/TimeSpanTranslationsNpgsqlTest.cs new file mode 100644 index 000000000..fbf2cb78e --- /dev/null +++ b/test/EFCore.PG.FunctionalTests/Query/Translations/Temporal/TimeSpanTranslationsNpgsqlTest.cs @@ -0,0 +1,72 @@ +namespace Microsoft.EntityFrameworkCore.Query.Translations.Temporal; + +public class TimeSpanTranslationsNpgsqlTest : TimeSpanTranslationsTestBase +{ + public TimeSpanTranslationsNpgsqlTest(BasicTypesQueryNpgsqlFixture fixture, ITestOutputHelper testOutputHelper) + : base(fixture) + { + Fixture.TestSqlLoggerFactory.Clear(); + Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); + } + + public override async Task Hours(bool async) + { + await base.Hours(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE floor(date_part('hour', b."TimeSpan"))::int = 3 +"""); + } + + public override async Task Minutes(bool async) + { + await base.Minutes(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE floor(date_part('minute', b."TimeSpan"))::int = 4 +"""); + } + + public override async Task Seconds(bool async) + { + await base.Seconds(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE floor(date_part('second', b."TimeSpan"))::int = 5 +"""); + } + + public override async Task Milliseconds(bool async) + { + await base.Milliseconds(async); + + AssertSql( + """ +SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" +FROM "BasicTypesEntities" AS b +WHERE floor(date_part('millisecond', b."TimeSpan"))::int % 1000 = 678 +"""); + } + + public override Task Microseconds(bool async) + => AssertTranslationFailed(() => base.Microseconds(async)); + + public override Task Nanoseconds(bool async) + => AssertTranslationFailed(() => base.Nanoseconds(async)); + + [ConditionalFact] + public virtual void Check_all_tests_overridden() + => TestHelpers.AssertAllMethodsOverridden(GetType()); + + private void AssertSql(params string[] expected) + => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); +} diff --git a/test/EFCore.PG.FunctionalTests/Query/Translations/TemporalTranslationsNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/Translations/TemporalTranslationsNpgsqlTest.cs deleted file mode 100644 index 1d966e4da..000000000 --- a/test/EFCore.PG.FunctionalTests/Query/Translations/TemporalTranslationsNpgsqlTest.cs +++ /dev/null @@ -1,940 +0,0 @@ -using Microsoft.EntityFrameworkCore.TestModels.BasicTypesModel; -using Xunit.Sdk; - -namespace Microsoft.EntityFrameworkCore.Query.Translations; - -/// -/// Note that is mapped to PG timestamp with time zone, as is the provider default; -/// this causes issues with various tests. See also , which -/// explicitly maps to timestamp without time zone. -/// -public class TemporalTranslationsNpgsqlTest : TemporalTranslationsTestBase -{ - public TemporalTranslationsNpgsqlTest(BasicTypesQueryNpgsqlFixture fixture, ITestOutputHelper testOutputHelper) - : base(fixture) - { - Fixture.TestSqlLoggerFactory.Clear(); - Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); - } - - #region DateTime - - public override async Task DateTime_Now(bool async) - { - await base.DateTime_Now(async); - - AssertSql( - """ -@myDatetime='2015-04-10T00:00:00.0000000' - -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE now()::timestamp <> @myDatetime -"""); - } - - public override async Task DateTime_UtcNow(bool async) - { - // Overriding to set Kind=Utc for timestamptz - var myDatetime = DateTime.SpecifyKind(new DateTime(2015, 4, 10), DateTimeKind.Utc); - - await AssertQuery( - async, - ss => ss.Set().Where(c => DateTime.UtcNow != myDatetime)); - - AssertSql( - """ -@myDatetime='2015-04-10T00:00:00.0000000Z' (DbType = DateTime) - -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE now() <> @myDatetime -"""); - } - - // DateTime.Today returns a Local DateTime, which can't be compared with timestamptz - // (see TemporalTranslationsNpgsqlTimestampWithoutTimeZoneTest for a working version of this test) - public override Task DateTime_Today(bool async) - => Assert.ThrowsAsync(() => base.DateTime_Today(async)); - - public override async Task DateTime_Date(bool async) - { - // Overriding to set Kind=Utc for timestamptz - var myDatetime = DateTime.SpecifyKind(new DateTime(1998, 5, 4), DateTimeKind.Utc); - - await AssertQuery( - async, - ss => ss.Set().Where(o => o.DateTime.Date == myDatetime)); - - AssertSql( - """ -@myDatetime='1998-05-04T00:00:00.0000000Z' (DbType = DateTime) - -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE date_trunc('day', b."DateTime", 'UTC') = @myDatetime -"""); - } - - public override async Task DateTime_AddYear(bool async) - { - await base.DateTime_AddYear(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE date_part('year', (b."DateTime" + INTERVAL '1 years') AT TIME ZONE 'UTC')::int = 1999 -"""); - } - - public override async Task DateTime_Year(bool async) - { - await base.DateTime_Year(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE date_part('year', b."DateTime" AT TIME ZONE 'UTC')::int = 1998 -"""); - } - - public override async Task DateTime_Month(bool async) - { - await base.DateTime_Month(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE date_part('month', b."DateTime" AT TIME ZONE 'UTC')::int = 5 -"""); - } - - public override async Task DateTime_DayOfYear(bool async) - { - await base.DateTime_DayOfYear(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE date_part('doy', b."DateTime" AT TIME ZONE 'UTC')::int = 124 -"""); - } - - public override async Task DateTime_Day(bool async) - { - await base.DateTime_Day(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE date_part('day', b."DateTime" AT TIME ZONE 'UTC')::int = 4 -"""); - } - - public override async Task DateTime_Hour(bool async) - { - await base.DateTime_Hour(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE date_part('hour', b."DateTime" AT TIME ZONE 'UTC')::int = 15 -"""); - } - - public override async Task DateTime_Minute(bool async) - { - await base.DateTime_Minute(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE date_part('minute', b."DateTime" AT TIME ZONE 'UTC')::int = 30 -"""); - } - - public override async Task DateTime_Second(bool async) - { - await base.DateTime_Second(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE date_part('second', b."DateTime" AT TIME ZONE 'UTC')::int = 10 -"""); - } - - // SQL translation not implemented, too annoying - public override Task DateTime_Millisecond(bool async) - => AssertTranslationFailed(() => base.DateTime_Millisecond(async)); - - public override async Task DateTime_TimeOfDay(bool async) - { - await base.DateTime_TimeOfDay(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE CAST(b."DateTime" AT TIME ZONE 'UTC' AS time) = TIME '00:00:00' -"""); - } - - public override async Task DateTime_subtract_and_TotalDays(bool async) - { - // Overriding to set Kind=Utc for timestamptz - var date = DateTime.SpecifyKind(new DateTime(1997, 1, 1), DateTimeKind.Utc); - - await AssertQuery( - async, - ss => ss.Set().Where(o => (o.DateTime - date).TotalDays > 365)); - - AssertSql( - """ -@date='1997-01-01T00:00:00.0000000Z' (DbType = DateTime) - -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE date_part('epoch', b."DateTime" - @date) / 86400.0 > 365.0 -"""); - } - - // DateTime.Parse() returns either a Local or Unspecified DateTime, which can't be compared with timestamptz - // (see TemporalTranslationsNpgsqlTimestampWithoutTimeZoneTest for a working version of this test) - public override Task DateTime_Parse_with_constant(bool async) - => Assert.ThrowsAsync(() => base.DateTime_Parse_with_constant(async)); - - // DateTime.Parse() returns either a Local or Unspecified DateTime, which can't be compared with timestamptz - // (see TemporalTranslationsNpgsqlTimestampWithoutTimeZoneTest for a working version of this test) - public override Task DateTime_Parse_with_parameter(bool async) - => Assert.ThrowsAsync(() => base.DateTime_Parse_with_parameter(async)); - - public override async Task DateTime_new_with_constant(bool async) - { - // Overriding to set Kind=Utc for timestamptz - await AssertQuery( - async, - ss => ss.Set().Where(o => o.DateTime == new DateTime(1998, 5, 4, 15, 30, 10, DateTimeKind.Utc))); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE b."DateTime" = TIMESTAMPTZ '1998-05-04T15:30:10Z' -"""); - } - - public override async Task DateTime_new_with_parameters(bool async) - { - // Overriding to set Kind=Utc for timestamptz - var year = 1998; - var month = 5; - var date = 4; - var hour = 15; - - await AssertQuery( - async, - ss => ss.Set().Where(o => o.DateTime == new DateTime(year, month, date, hour, 30, 10, DateTimeKind.Utc))); - - AssertSql( - """ -@p='1998-05-04T15:30:10.0000000Z' (DbType = DateTime) - -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE b."DateTime" = @p -"""); - } - - #endregion DateTime - - #region DateOnly - - public override async Task DateOnly_Year(bool async) - { - await base.DateOnly_Year(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE date_part('year', b."DateOnly")::int = 1990 -"""); - } - - public override async Task DateOnly_Month(bool async) - { - await base.DateOnly_Month(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE date_part('month', b."DateOnly")::int = 11 -"""); - } - - public override async Task DateOnly_Day(bool async) - { - await base.DateOnly_Day(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE date_part('day', b."DateOnly")::int = 10 -"""); - } - - public override async Task DateOnly_DayOfYear(bool async) - { - await base.DateOnly_DayOfYear(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE date_part('doy', b."DateOnly")::int = 314 -"""); - } - - public override async Task DateOnly_DayOfWeek(bool async) - { - await base.DateOnly_DayOfWeek(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE floor(date_part('dow', b."DateOnly"))::int = 6 -"""); - } - - public override async Task DateOnly_AddYears(bool async) - { - await base.DateOnly_AddYears(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE CAST(b."DateOnly" + INTERVAL '3 years' AS date) = DATE '1993-11-10' -"""); - } - - public override async Task DateOnly_AddMonths(bool async) - { - await base.DateOnly_AddMonths(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE CAST(b."DateOnly" + INTERVAL '3 months' AS date) = DATE '1991-02-10' -"""); - } - - public override async Task DateOnly_AddDays(bool async) - { - await base.DateOnly_AddDays(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE b."DateOnly" + 3 = DATE '1990-11-13' -"""); - } - - public override async Task DateOnly_FromDateTime(bool async) - { - await base.DateOnly_FromDateTime(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE CAST(b."DateTime" AT TIME ZONE 'UTC' AS date) = DATE '1998-05-04' -"""); - } - - public override async Task DateOnly_FromDateTime_compared_to_property(bool async) - { - await base.DateOnly_FromDateTime_compared_to_property(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE CAST(b."DateTime" AT TIME ZONE 'UTC' AS date) = b."DateOnly" -"""); - } - - public override async Task DateOnly_FromDateTime_compared_to_constant_and_parameter(bool async) - { - await base.DateOnly_FromDateTime_compared_to_constant_and_parameter(async); - - AssertSql( - """ -@dateOnly='10/11/0002' (DbType = Date) - -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE CAST(b."DateTime" AT TIME ZONE 'UTC' AS date) IN (@dateOnly, DATE '1998-05-04') -"""); - } - - public override async Task DateOnly_ToDateTime_property_DateOnly_with_constant_TimeOnly(bool async) - { - await base.DateOnly_ToDateTime_property_DateOnly_with_constant_TimeOnly(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE b."DateOnly" + TIME '21:05:19.9405' = TIMESTAMP '2020-01-01T21:05:19.9405' -"""); - } - - public override async Task DateOnly_ToDateTime_property_DateOnly_with_property_TimeOnly(bool async) - { - await base.DateOnly_ToDateTime_property_DateOnly_with_property_TimeOnly(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE b."DateOnly" + b."TimeOnly" = TIMESTAMP '2020-01-01T15:30:10' -"""); - } - - public override async Task DateOnly_ToDateTime_constant_DateTime_with_property_TimeOnly(bool async) - { - await base.DateOnly_ToDateTime_constant_DateTime_with_property_TimeOnly(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE DATE '1990-11-10' + b."TimeOnly" = TIMESTAMP '1990-11-10T15:30:10' -"""); - } - - public override async Task DateOnly_ToDateTime_with_complex_DateTime(bool async) - { - await base.DateOnly_ToDateTime_with_complex_DateTime(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE CAST(b."DateOnly" + INTERVAL '1 years' AS date) + b."TimeOnly" = TIMESTAMP '2021-01-01T15:30:10' -"""); - } - - public override async Task DateOnly_ToDateTime_with_complex_TimeOnly(bool async) - { - await base.DateOnly_ToDateTime_with_complex_TimeOnly(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE b."DateOnly" + b."TimeOnly" + INTERVAL '1 hours' = TIMESTAMP '2020-01-01T16:30:10' -"""); - } - - #endregion DateOnly - - #region TimeOnly - - public override async Task TimeOnly_Hour(bool async) - { - await base.TimeOnly_Hour(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE date_part('hour', b."TimeOnly")::int = 15 -"""); - } - - public override async Task TimeOnly_Minute(bool async) - { - await base.TimeOnly_Minute(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE date_part('minute', b."TimeOnly")::int = 30 -"""); - } - - public override async Task TimeOnly_Second(bool async) - { - await base.TimeOnly_Second(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE date_part('second', b."TimeOnly")::int = 10 -"""); - } - - // Translation not yet implemented - public override Task TimeOnly_Millisecond(bool async) - => AssertTranslationFailed(() => base.TimeOnly_Millisecond(async)); - - // Translation not yet implemented - public override Task TimeOnly_Microsecond(bool async) - => AssertTranslationFailed(() => base.TimeOnly_Millisecond(async)); - - // Probably not relevant for PostgreSQL, which supports microsecond precision only - public override Task TimeOnly_Nanosecond(bool async) - => AssertTranslationFailed(() => base.TimeOnly_Millisecond(async)); - - public override async Task TimeOnly_AddHours(bool async) - { - await base.TimeOnly_AddHours(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE b."TimeOnly" + INTERVAL '3 hours' = TIME '18:30:10' -"""); - } - - public override async Task TimeOnly_AddMinutes(bool async) - { - await base.TimeOnly_AddMinutes(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE b."TimeOnly" + INTERVAL '3 mins' = TIME '15:33:10' -"""); - } - - public override async Task TimeOnly_Add_TimeSpan(bool async) - { - await base.TimeOnly_Add_TimeSpan(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE b."TimeOnly" + INTERVAL '03:00:00' = TIME '18:30:10' -"""); - } - - public override async Task TimeOnly_IsBetween(bool async) - { - await base.TimeOnly_IsBetween(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE b."TimeOnly" >= TIME '14:00:00' AND b."TimeOnly" < TIME '16:00:00' -"""); - } - - public override async Task TimeOnly_subtract_TimeOnly(bool async) - { - await base.TimeOnly_subtract_TimeOnly(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE b."TimeOnly" - TIME '03:00:00' = INTERVAL '12:30:10' -"""); - } - - public override async Task TimeOnly_FromDateTime_compared_to_property(bool async) - { - await base.TimeOnly_FromDateTime_compared_to_property(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE CAST(b."DateTime" AT TIME ZONE 'UTC' AS time without time zone) = b."TimeOnly" -"""); - } - - public override async Task TimeOnly_FromDateTime_compared_to_parameter(bool async) - { - await base.TimeOnly_FromDateTime_compared_to_parameter(async); - - AssertSql( - """ -@time='15:30' (DbType = Time) - -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE CAST(b."DateTime" AT TIME ZONE 'UTC' AS time without time zone) = @time -"""); - } - - public override async Task TimeOnly_FromDateTime_compared_to_constant(bool async) - { - await base.TimeOnly_FromDateTime_compared_to_constant(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE CAST(b."DateTime" AT TIME ZONE 'UTC' AS time without time zone) = TIME '15:30:10' -"""); - } - - public override async Task TimeOnly_FromTimeSpan_compared_to_property(bool async) - { - await base.TimeOnly_FromTimeSpan_compared_to_property(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE b."TimeSpan"::time without time zone < b."TimeOnly" -"""); - } - - public override async Task TimeOnly_FromTimeSpan_compared_to_parameter(bool async) - { - await base.TimeOnly_FromTimeSpan_compared_to_parameter(async); - - AssertSql( - """ -@time='01:02' (DbType = Time) - -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE b."TimeSpan"::time without time zone = @time -"""); - } - - public override async Task Order_by_TimeOnly_FromTimeSpan(bool async) - { - // TODO: Base implementation is non-deterministic, remove this override once that's fixed on the EF side. - await AssertQuery( - async, - ss => ss.Set().OrderBy(x => TimeOnly.FromTimeSpan(x.TimeSpan)).ThenBy(x => x.Id), - assertOrder: true); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -ORDER BY b."TimeSpan"::time without time zone NULLS FIRST, b."Id" NULLS FIRST -"""); - } - - #endregion TimeOnly - - #region DateTimeOffset - - // Not supported by design (DateTimeOffset with non-zero offset) - public override Task DateTimeOffset_Now(bool async) - => Assert.ThrowsAsync(() => base.DateTimeOffset_Now(async)); - - public override async Task DateTimeOffset_UtcNow(bool async) - { - await base.DateTimeOffset_UtcNow(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE b."DateTimeOffset" <> now() -"""); - } - - // The test compares with new DateTimeOffset().Date, which Npgsql sends as -infinity, causing a discrepancy with the client behavior - // which uses 1/1/1:0:0:0 - public override Task DateTimeOffset_Date(bool async) - => Assert.ThrowsAsync(() => base.DateTimeOffset_Date(async)); - - public override async Task DateTimeOffset_Year(bool async) - { - await base.DateTimeOffset_Year(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE date_part('year', b."DateTimeOffset" AT TIME ZONE 'UTC')::int = 1998 -"""); - } - - public override async Task DateTimeOffset_Month(bool async) - { - await base.DateTimeOffset_Month(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE date_part('month', b."DateTimeOffset" AT TIME ZONE 'UTC')::int = 5 -"""); - } - - public override async Task DateTimeOffset_DayOfYear(bool async) - { - await base.DateTimeOffset_DayOfYear(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE date_part('doy', b."DateTimeOffset" AT TIME ZONE 'UTC')::int = 124 -"""); - } - - public override async Task DateTimeOffset_Day(bool async) - { - await base.DateTimeOffset_Day(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE date_part('day', b."DateTimeOffset" AT TIME ZONE 'UTC')::int = 4 -"""); - } - - public override async Task DateTimeOffset_Hour(bool async) - { - await base.DateTimeOffset_Hour(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE date_part('hour', b."DateTimeOffset" AT TIME ZONE 'UTC')::int = 15 -"""); - } - - public override async Task DateTimeOffset_Minute(bool async) - { - await base.DateTimeOffset_Minute(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE date_part('minute', b."DateTimeOffset" AT TIME ZONE 'UTC')::int = 30 -"""); - } - - public override async Task DateTimeOffset_Second(bool async) - { - await base.DateTimeOffset_Second(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE date_part('second', b."DateTimeOffset" AT TIME ZONE 'UTC')::int = 10 -"""); - } - - // SQL translation not implemented, too annoying - public override Task DateTimeOffset_Millisecond(bool async) - => AssertTranslationFailed(() => base.DateTimeOffset_Millisecond(async)); - - // TODO: #3406 - public override Task DateTimeOffset_Microsecond(bool async) - => AssertTranslationFailed(() => base.DateTimeOffset_Microsecond(async)); - - // TODO: #3406 - public override Task DateTimeOffset_Nanosecond(bool async) - => AssertTranslationFailed(() => base.DateTimeOffset_Nanosecond(async)); - - public override async Task DateTimeOffset_TimeOfDay(bool async) - { - await base.DateTimeOffset_TimeOfDay(async); - - AssertSql( - """ -SELECT CAST(b."DateTimeOffset" AT TIME ZONE 'UTC' AS time) -FROM "BasicTypesEntities" AS b -"""); - } - - public override async Task DateTimeOffset_AddYears(bool async) - { - await base.DateTimeOffset_AddYears(async); - - AssertSql( - """ -SELECT b."DateTimeOffset" + INTERVAL '1 years' -FROM "BasicTypesEntities" AS b -"""); - } - - public override async Task DateTimeOffset_AddMonths(bool async) - { - await base.DateTimeOffset_AddMonths(async); - - AssertSql( - """ -SELECT b."DateTimeOffset" + INTERVAL '1 months' -FROM "BasicTypesEntities" AS b -"""); - } - - public override async Task DateTimeOffset_AddDays(bool async) - { - await base.DateTimeOffset_AddDays(async); - - AssertSql( - """ -SELECT b."DateTimeOffset" + INTERVAL '1 days' -FROM "BasicTypesEntities" AS b -"""); - } - - public override async Task DateTimeOffset_AddHours(bool async) - { - await base.DateTimeOffset_AddHours(async); - - AssertSql( - """ -SELECT b."DateTimeOffset" + INTERVAL '1 hours' -FROM "BasicTypesEntities" AS b -"""); - } - - public override async Task DateTimeOffset_AddMinutes(bool async) - { - await base.DateTimeOffset_AddMinutes(async); - - AssertSql( - """ -SELECT b."DateTimeOffset" + INTERVAL '1 mins' -FROM "BasicTypesEntities" AS b -"""); - } - - public override async Task DateTimeOffset_AddSeconds(bool async) - { - await base.DateTimeOffset_AddSeconds(async); - - AssertSql( - """ -SELECT b."DateTimeOffset" + INTERVAL '1 secs' -FROM "BasicTypesEntities" AS b -"""); - } - - public override async Task DateTimeOffset_AddMilliseconds(bool async) - { - await base.DateTimeOffset_AddMilliseconds(async); - - AssertSql( - """ -SELECT b."DateTimeOffset" -FROM "BasicTypesEntities" AS b -"""); - } - - public override Task DateTimeOffset_ToUnixTimeMilliseconds(bool async) - => AssertTranslationFailed(() => base.DateTimeOffset_ToUnixTimeMilliseconds(async)); - - public override Task DateTimeOffset_ToUnixTimeSecond(bool async) - => AssertTranslationFailed(() => base.DateTimeOffset_ToUnixTimeSecond(async)); - - public override async Task DateTimeOffset_milliseconds_parameter_and_constant(bool async) - { - await base.DateTimeOffset_milliseconds_parameter_and_constant(async); - - AssertSql( - """ -SELECT count(*)::int -FROM "BasicTypesEntities" AS b -WHERE b."DateTimeOffset" = TIMESTAMPTZ '1902-01-02T10:00:00.123456+01:30' -"""); - } - - #endregion DateTimeOffset - - #region TimeSpan - - public override async Task TimeSpan_Hours(bool async) - { - await base.TimeSpan_Hours(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE floor(date_part('hour', b."TimeSpan"))::int = 3 -"""); - } - - public override async Task TimeSpan_Minutes(bool async) - { - await base.TimeSpan_Minutes(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE floor(date_part('minute', b."TimeSpan"))::int = 4 -"""); - } - - public override async Task TimeSpan_Seconds(bool async) - { - await base.TimeSpan_Seconds(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE floor(date_part('second', b."TimeSpan"))::int = 5 -"""); - } - - public override async Task TimeSpan_Milliseconds(bool async) - { - await base.TimeSpan_Milliseconds(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE floor(date_part('millisecond', b."TimeSpan"))::int % 1000 = 678 -"""); - } - - public override Task TimeSpan_Microseconds(bool async) - => AssertTranslationFailed(() => base.TimeSpan_Microseconds(async)); - - public override Task TimeSpan_Nanoseconds(bool async) - => AssertTranslationFailed(() => base.TimeSpan_Nanoseconds(async)); - - #endregion TimeSpan - - [ConditionalFact] - public virtual void Check_all_tests_overridden() - => TestHelpers.AssertAllMethodsOverridden(GetType()); - - private void AssertSql(params string[] expected) - => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); -} diff --git a/test/EFCore.PG.FunctionalTests/Query/Translations/TemporalTranslationsNpgsqlTimestampWithoutTimeZoneTest.cs b/test/EFCore.PG.FunctionalTests/Query/Translations/TemporalTranslationsNpgsqlTimestampWithoutTimeZoneTest.cs deleted file mode 100644 index 3a9d64072..000000000 --- a/test/EFCore.PG.FunctionalTests/Query/Translations/TemporalTranslationsNpgsqlTimestampWithoutTimeZoneTest.cs +++ /dev/null @@ -1,995 +0,0 @@ -using Microsoft.EntityFrameworkCore.TestModels.BasicTypesModel; -using Xunit.Sdk; - -namespace Microsoft.EntityFrameworkCore.Query.Translations; - -/// -/// Same as , but the property is mapped to a PostgreSQL -/// timestamp without time zone, which corresponds to a with Kind -/// . -/// -public class TemporalTranslationsNpgsqlTimestampWithoutTimeZoneTest - : TemporalTranslationsTestBase -{ - public TemporalTranslationsNpgsqlTimestampWithoutTimeZoneTest( - BasicTypesQueryNpgsqlTimestampWithoutTimeZoneFixture fixture, - ITestOutputHelper testOutputHelper) - : base(fixture) - { - Fixture.TestSqlLoggerFactory.Clear(); - Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); - } - - #region DateTime - - public override async Task DateTime_Now(bool async) - { - await base.DateTime_Now(async); - - AssertSql( - """ -@myDatetime='2015-04-10T00:00:00.0000000' - -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE now()::timestamp <> @myDatetime -"""); - } - - public override async Task DateTime_UtcNow(bool async) - { - // Overriding to set Kind=Utc for timestamptz. This test generally doesn't make much sense here. - var myDatetime = DateTime.SpecifyKind(new DateTime(2015, 4, 10), DateTimeKind.Utc); - - await AssertQuery( - async, - ss => ss.Set().Where(c => DateTime.UtcNow != myDatetime)); - - AssertSql( - """ -@myDatetime='2015-04-10T00:00:00.0000000Z' (DbType = DateTime) - -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE now() <> @myDatetime -"""); - } - - public override async Task DateTime_Today(bool async) - { - await base.DateTime_Today(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE b."DateTime" = date_trunc('day', now()::timestamp) -"""); - } - - public override async Task DateTime_Date(bool async) - { - await base.DateTime_Date(async); - - AssertSql( - """ -@myDatetime='1998-05-04T00:00:00.0000000' - -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE date_trunc('day', b."DateTime") = @myDatetime -"""); - } - - public override async Task DateTime_AddYear(bool async) - { - await base.DateTime_AddYear(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE date_part('year', b."DateTime" + INTERVAL '1 years')::int = 1999 -"""); - } - - public override async Task DateTime_Year(bool async) - { - await base.DateTime_Year(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE date_part('year', b."DateTime")::int = 1998 -"""); - } - - public override async Task DateTime_Month(bool async) - { - await base.DateTime_Month(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE date_part('month', b."DateTime")::int = 5 -"""); - } - - public override async Task DateTime_DayOfYear(bool async) - { - await base.DateTime_DayOfYear(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE date_part('doy', b."DateTime")::int = 124 -"""); - } - - public override async Task DateTime_Day(bool async) - { - await base.DateTime_Day(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE date_part('day', b."DateTime")::int = 4 -"""); - } - - public override async Task DateTime_Hour(bool async) - { - await base.DateTime_Hour(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE date_part('hour', b."DateTime")::int = 15 -"""); - } - - public override async Task DateTime_Minute(bool async) - { - await base.DateTime_Minute(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE date_part('minute', b."DateTime")::int = 30 -"""); - } - - public override async Task DateTime_Second(bool async) - { - await base.DateTime_Second(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE date_part('second', b."DateTime")::int = 10 -"""); - } - - // SQL translation not implemented, too annoying - public override Task DateTime_Millisecond(bool async) - => AssertTranslationFailed(() => base.DateTime_Millisecond(async)); - - public override async Task DateTime_TimeOfDay(bool async) - { - await base.DateTime_TimeOfDay(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE b."DateTime"::time = TIME '00:00:00' -"""); - } - - public override async Task DateTime_subtract_and_TotalDays(bool async) - { - await base.DateTime_subtract_and_TotalDays(async); - - AssertSql( - """ -@date='1997-01-01T00:00:00.0000000' - -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE date_part('epoch', b."DateTime" - @date) / 86400.0 > 365.0 -"""); - } - - public override async Task DateTime_Parse_with_constant(bool async) - { - await base.DateTime_Parse_with_constant(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE b."DateTime" = TIMESTAMP '1998-05-04T15:30:10' -"""); - } - - public override async Task DateTime_Parse_with_parameter(bool async) - { - await base.DateTime_Parse_with_parameter(async); - - AssertSql( - """ -@Parse='1998-05-04T15:30:10.0000000' - -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE b."DateTime" = @Parse -"""); - } - - public override async Task DateTime_new_with_constant(bool async) - { - await base.DateTime_new_with_constant(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE b."DateTime" = TIMESTAMP '1998-05-04T15:30:10' -"""); - } - - public override async Task DateTime_new_with_parameters(bool async) - { - await base.DateTime_new_with_parameters(async); - - AssertSql( - """ -@p='1998-05-04T15:30:10.0000000' - -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE b."DateTime" = @p -"""); - } - - #endregion DateTime - - #region DateOnly - - public override async Task DateOnly_Year(bool async) - { - await base.DateOnly_Year(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE date_part('year', b."DateOnly")::int = 1990 -"""); - } - - public override async Task DateOnly_Month(bool async) - { - await base.DateOnly_Month(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE date_part('month', b."DateOnly")::int = 11 -"""); - } - - public override async Task DateOnly_Day(bool async) - { - await base.DateOnly_Day(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE date_part('day', b."DateOnly")::int = 10 -"""); - } - - public override async Task DateOnly_DayOfYear(bool async) - { - await base.DateOnly_DayOfYear(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE date_part('doy', b."DateOnly")::int = 314 -"""); - } - - public override async Task DateOnly_DayOfWeek(bool async) - { - await base.DateOnly_DayOfWeek(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE floor(date_part('dow', b."DateOnly"))::int = 6 -"""); - } - - public override async Task DateOnly_AddYears(bool async) - { - await base.DateOnly_AddYears(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE CAST(b."DateOnly" + INTERVAL '3 years' AS date) = DATE '1993-11-10' -"""); - } - - public override async Task DateOnly_AddMonths(bool async) - { - await base.DateOnly_AddMonths(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE CAST(b."DateOnly" + INTERVAL '3 months' AS date) = DATE '1991-02-10' -"""); - } - - public override async Task DateOnly_AddDays(bool async) - { - await base.DateOnly_AddDays(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE b."DateOnly" + 3 = DATE '1990-11-13' -"""); - } - - public override async Task DateOnly_FromDateTime(bool async) - { - await base.DateOnly_FromDateTime(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE b."DateTime"::date = DATE '1998-05-04' -"""); - } - - public override async Task DateOnly_FromDateTime_compared_to_property(bool async) - { - await base.DateOnly_FromDateTime_compared_to_property(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE b."DateTime"::date = b."DateOnly" -"""); - } - - public override async Task DateOnly_FromDateTime_compared_to_constant_and_parameter(bool async) - { - await base.DateOnly_FromDateTime_compared_to_constant_and_parameter(async); - - AssertSql( - """ -@dateOnly='10/11/0002' (DbType = Date) - -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE b."DateTime"::date IN (@dateOnly, DATE '1998-05-04') -"""); - } - - public override async Task DateOnly_ToDateTime_property_DateOnly_with_constant_TimeOnly(bool async) - { - await base.DateOnly_ToDateTime_property_DateOnly_with_constant_TimeOnly(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE b."DateOnly" + TIME '21:05:19.9405' = TIMESTAMP '2020-01-01T21:05:19.9405' -"""); - } - - public override async Task DateOnly_ToDateTime_property_DateOnly_with_property_TimeOnly(bool async) - { - await base.DateOnly_ToDateTime_property_DateOnly_with_property_TimeOnly(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE b."DateOnly" + b."TimeOnly" = TIMESTAMP '2020-01-01T15:30:10' -"""); - } - - public override async Task DateOnly_ToDateTime_constant_DateTime_with_property_TimeOnly(bool async) - { - await base.DateOnly_ToDateTime_constant_DateTime_with_property_TimeOnly(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE DATE '1990-11-10' + b."TimeOnly" = TIMESTAMP '1990-11-10T15:30:10' -"""); - } - - public override async Task DateOnly_ToDateTime_with_complex_DateTime(bool async) - { - await base.DateOnly_ToDateTime_with_complex_DateTime(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE CAST(b."DateOnly" + INTERVAL '1 years' AS date) + b."TimeOnly" = TIMESTAMP '2021-01-01T15:30:10' -"""); - } - - public override async Task DateOnly_ToDateTime_with_complex_TimeOnly(bool async) - { - await base.DateOnly_ToDateTime_with_complex_TimeOnly(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE b."DateOnly" + b."TimeOnly" + INTERVAL '1 hours' = TIMESTAMP '2020-01-01T16:30:10' -"""); - } - - #endregion DateOnly - - #region TimeOnly - - public override async Task TimeOnly_Hour(bool async) - { - await base.TimeOnly_Hour(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE date_part('hour', b."TimeOnly")::int = 15 -"""); - } - - public override async Task TimeOnly_Minute(bool async) - { - await base.TimeOnly_Minute(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE date_part('minute', b."TimeOnly")::int = 30 -"""); - } - - public override async Task TimeOnly_Second(bool async) - { - await base.TimeOnly_Second(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE date_part('second', b."TimeOnly")::int = 10 -"""); - } - - // Translation not yet implemented - public override Task TimeOnly_Millisecond(bool async) - => AssertTranslationFailed(() => base.TimeOnly_Millisecond(async)); - - // Translation not yet implemented - public override Task TimeOnly_Microsecond(bool async) - => AssertTranslationFailed(() => base.TimeOnly_Millisecond(async)); - - // Probably not relevant for PostgreSQL, which supports microsecond precision only - public override Task TimeOnly_Nanosecond(bool async) - => AssertTranslationFailed(() => base.TimeOnly_Millisecond(async)); - - public override async Task TimeOnly_AddHours(bool async) - { - await base.TimeOnly_AddHours(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE b."TimeOnly" + INTERVAL '3 hours' = TIME '18:30:10' -"""); - } - - public override async Task TimeOnly_AddMinutes(bool async) - { - await base.TimeOnly_AddMinutes(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE b."TimeOnly" + INTERVAL '3 mins' = TIME '15:33:10' -"""); - } - - public override async Task TimeOnly_Add_TimeSpan(bool async) - { - await base.TimeOnly_Add_TimeSpan(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE b."TimeOnly" + INTERVAL '03:00:00' = TIME '18:30:10' -"""); - } - - public override async Task TimeOnly_IsBetween(bool async) - { - await base.TimeOnly_IsBetween(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE b."TimeOnly" >= TIME '14:00:00' AND b."TimeOnly" < TIME '16:00:00' -"""); - } - - public override async Task TimeOnly_subtract_TimeOnly(bool async) - { - await base.TimeOnly_subtract_TimeOnly(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE b."TimeOnly" - TIME '03:00:00' = INTERVAL '12:30:10' -"""); - } - - public override async Task TimeOnly_FromDateTime_compared_to_property(bool async) - { - await base.TimeOnly_FromDateTime_compared_to_property(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE b."DateTime"::time without time zone = b."TimeOnly" -"""); - } - - public override async Task TimeOnly_FromDateTime_compared_to_parameter(bool async) - { - await base.TimeOnly_FromDateTime_compared_to_parameter(async); - - AssertSql( - """ -@time='15:30' (DbType = Time) - -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE b."DateTime"::time without time zone = @time -"""); - } - - public override async Task TimeOnly_FromDateTime_compared_to_constant(bool async) - { - await base.TimeOnly_FromDateTime_compared_to_constant(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE b."DateTime"::time without time zone = TIME '15:30:10' -"""); - } - - public override async Task TimeOnly_FromTimeSpan_compared_to_property(bool async) - { - await base.TimeOnly_FromTimeSpan_compared_to_property(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE b."TimeSpan"::time without time zone < b."TimeOnly" -"""); - } - - public override async Task TimeOnly_FromTimeSpan_compared_to_parameter(bool async) - { - await base.TimeOnly_FromTimeSpan_compared_to_parameter(async); - - AssertSql( - """ -@time='01:02' (DbType = Time) - -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE b."TimeSpan"::time without time zone = @time -"""); - } - - public override async Task Order_by_TimeOnly_FromTimeSpan(bool async) - { - // TODO: Base implementation is non-deterministic, remove this override once that's fixed on the EF side. - await AssertQuery( - async, - ss => ss.Set().OrderBy(x => TimeOnly.FromTimeSpan(x.TimeSpan)).ThenBy(x => x.Id), - assertOrder: true); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -ORDER BY b."TimeSpan"::time without time zone NULLS FIRST, b."Id" NULLS FIRST -"""); - } - - #endregion TimeOnly - - #region DateTimeOffset - - // Not supported by design (DateTimeOffset with non-zero offset) - public override Task DateTimeOffset_Now(bool async) - => Assert.ThrowsAsync(() => base.DateTimeOffset_Now(async)); - - public override async Task DateTimeOffset_UtcNow(bool async) - { - await base.DateTimeOffset_UtcNow(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE b."DateTimeOffset" <> now() -"""); - } - - // The test compares with new DateTimeOffset().Date, which Npgsql sends as -infinity, causing a discrepancy with the client behavior - // which uses 1/1/1:0:0:0 - public override Task DateTimeOffset_Date(bool async) - => Assert.ThrowsAsync(() => base.DateTimeOffset_Date(async)); - - public override async Task DateTimeOffset_Year(bool async) - { - await base.DateTimeOffset_Year(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE date_part('year', b."DateTimeOffset" AT TIME ZONE 'UTC')::int = 1998 -"""); - } - - public override async Task DateTimeOffset_Month(bool async) - { - await base.DateTimeOffset_Month(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE date_part('month', b."DateTimeOffset" AT TIME ZONE 'UTC')::int = 5 -"""); - } - - public override async Task DateTimeOffset_DayOfYear(bool async) - { - await base.DateTimeOffset_DayOfYear(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE date_part('doy', b."DateTimeOffset" AT TIME ZONE 'UTC')::int = 124 -"""); - } - - public override async Task DateTimeOffset_Day(bool async) - { - await base.DateTimeOffset_Day(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE date_part('day', b."DateTimeOffset" AT TIME ZONE 'UTC')::int = 4 -"""); - } - - public override async Task DateTimeOffset_Hour(bool async) - { - await base.DateTimeOffset_Hour(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE date_part('hour', b."DateTimeOffset" AT TIME ZONE 'UTC')::int = 15 -"""); - } - - public override async Task DateTimeOffset_Minute(bool async) - { - await base.DateTimeOffset_Minute(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE date_part('minute', b."DateTimeOffset" AT TIME ZONE 'UTC')::int = 30 -"""); - } - - public override async Task DateTimeOffset_Second(bool async) - { - await base.DateTimeOffset_Second(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE date_part('second', b."DateTimeOffset" AT TIME ZONE 'UTC')::int = 10 -"""); - } - - // SQL translation not implemented, too annoying - public override Task DateTimeOffset_Millisecond(bool async) - => AssertTranslationFailed(() => base.DateTimeOffset_Millisecond(async)); - - // TODO: #3406 - public override Task DateTimeOffset_Microsecond(bool async) - => AssertTranslationFailed(() => base.DateTimeOffset_Microsecond(async)); - - // TODO: #3406 - public override Task DateTimeOffset_Nanosecond(bool async) - => AssertTranslationFailed(() => base.DateTimeOffset_Nanosecond(async)); - - public override async Task DateTimeOffset_TimeOfDay(bool async) - { - await base.DateTimeOffset_TimeOfDay(async); - - AssertSql( - """ -SELECT CAST(b."DateTimeOffset" AT TIME ZONE 'UTC' AS time) -FROM "BasicTypesEntities" AS b -"""); - } - - public override async Task DateTimeOffset_AddYears(bool async) - { - await base.DateTimeOffset_AddYears(async); - - AssertSql( - """ -SELECT b."DateTimeOffset" + INTERVAL '1 years' -FROM "BasicTypesEntities" AS b -"""); - } - - public override async Task DateTimeOffset_AddMonths(bool async) - { - await base.DateTimeOffset_AddMonths(async); - - AssertSql( - """ -SELECT b."DateTimeOffset" + INTERVAL '1 months' -FROM "BasicTypesEntities" AS b -"""); - } - - public override async Task DateTimeOffset_AddDays(bool async) - { - await base.DateTimeOffset_AddDays(async); - - AssertSql( - """ -SELECT b."DateTimeOffset" + INTERVAL '1 days' -FROM "BasicTypesEntities" AS b -"""); - } - - public override async Task DateTimeOffset_AddHours(bool async) - { - await base.DateTimeOffset_AddHours(async); - - AssertSql( - """ -SELECT b."DateTimeOffset" + INTERVAL '1 hours' -FROM "BasicTypesEntities" AS b -"""); - } - - public override async Task DateTimeOffset_AddMinutes(bool async) - { - await base.DateTimeOffset_AddMinutes(async); - - AssertSql( - """ -SELECT b."DateTimeOffset" + INTERVAL '1 mins' -FROM "BasicTypesEntities" AS b -"""); - } - - public override async Task DateTimeOffset_AddSeconds(bool async) - { - await base.DateTimeOffset_AddSeconds(async); - - AssertSql( - """ -SELECT b."DateTimeOffset" + INTERVAL '1 secs' -FROM "BasicTypesEntities" AS b -"""); - } - - public override async Task DateTimeOffset_AddMilliseconds(bool async) - { - await base.DateTimeOffset_AddMilliseconds(async); - - AssertSql( - """ -SELECT b."DateTimeOffset" -FROM "BasicTypesEntities" AS b -"""); - } - - public override Task DateTimeOffset_ToUnixTimeMilliseconds(bool async) - => AssertTranslationFailed(() => base.DateTimeOffset_ToUnixTimeMilliseconds(async)); - - public override Task DateTimeOffset_ToUnixTimeSecond(bool async) - => AssertTranslationFailed(() => base.DateTimeOffset_ToUnixTimeSecond(async)); - - // SQL translation not implemented, too annoying - public override async Task DateTimeOffset_milliseconds_parameter_and_constant(bool async) - { - await base.DateTimeOffset_milliseconds_parameter_and_constant(async); - - AssertSql( - """ -SELECT count(*)::int -FROM "BasicTypesEntities" AS b -WHERE b."DateTimeOffset" = TIMESTAMPTZ '1902-01-02T10:00:00.123456+01:30' -"""); - } - - #endregion DateTimeOffset - - #region TimeSpan - - public override async Task TimeSpan_Hours(bool async) - { - await base.TimeSpan_Hours(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE floor(date_part('hour', b."TimeSpan"))::int = 3 -"""); - } - - public override async Task TimeSpan_Minutes(bool async) - { - await base.TimeSpan_Minutes(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE floor(date_part('minute', b."TimeSpan"))::int = 4 -"""); - } - - public override async Task TimeSpan_Seconds(bool async) - { - await base.TimeSpan_Seconds(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE floor(date_part('second', b."TimeSpan"))::int = 5 -"""); - } - - public override async Task TimeSpan_Milliseconds(bool async) - { - await base.TimeSpan_Milliseconds(async); - - AssertSql( - """ -SELECT b."Id", b."Bool", b."Byte", b."ByteArray", b."DateOnly", b."DateTime", b."DateTimeOffset", b."Decimal", b."Double", b."Enum", b."FlagsEnum", b."Float", b."Guid", b."Int", b."Long", b."Short", b."String", b."TimeOnly", b."TimeSpan" -FROM "BasicTypesEntities" AS b -WHERE floor(date_part('millisecond', b."TimeSpan"))::int % 1000 = 678 -"""); - } - - public override Task TimeSpan_Microseconds(bool async) - => AssertTranslationFailed(() => base.TimeSpan_Microseconds(async)); - - public override Task TimeSpan_Nanoseconds(bool async) - => AssertTranslationFailed(() => base.TimeSpan_Nanoseconds(async)); - - #endregion TimeSpan - - [ConditionalFact] - public virtual void Check_all_tests_overridden() - => TestHelpers.AssertAllMethodsOverridden(GetType()); - - private void AssertSql(params string[] expected) - => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); - - public class BasicTypesQueryNpgsqlTimestampWithoutTimeZoneFixture : BasicTypesQueryNpgsqlFixture - { - private BasicTypesData? _expectedData; - - protected override string StoreName - => "BasicTypesTimestampWithoutTimeZoneTest"; - - protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext context) - { - base.OnModelCreating(modelBuilder, context); - - modelBuilder.Entity().Property(b => b.DateTime).HasColumnType("timestamp without time zone"); - modelBuilder.Entity().Property(b => b.DateTime).HasColumnType("timestamp without time zone"); - } - - protected override Task SeedAsync(BasicTypesContext context) - { - _expectedData ??= LoadAndTweakData(); - context.AddRange(_expectedData.BasicTypesEntities); - context.AddRange(_expectedData.NullableBasicTypesEntities); - return context.SaveChangesAsync(); - } - - public override ISetSource GetExpectedData() - => _expectedData ??= LoadAndTweakData(); - - private BasicTypesData LoadAndTweakData() - { - var data = (BasicTypesData)base.GetExpectedData(); - - foreach (var item in data.BasicTypesEntities) - { - // Change Kind fo all DateTimes from Utc to Unspecified, as we're mapping to 'timestamp without time zone' - item.DateTime = DateTime.SpecifyKind(item.DateTime, DateTimeKind.Unspecified); - } - - // Do the same for the nullable counterparts - foreach (var item in data.NullableBasicTypesEntities) - { - if (item.DateTime.HasValue) - { - item.DateTime = DateTime.SpecifyKind(item.DateTime.Value, DateTimeKind.Unspecified); - } - } - - return data; - } - } -} diff --git a/test/EFCore.PG.FunctionalTests/TableSplittingNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/TableSplittingNpgsqlTest.cs index ba9f25e1a..a53c1955d 100644 --- a/test/EFCore.PG.FunctionalTests/TableSplittingNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/TableSplittingNpgsqlTest.cs @@ -14,8 +14,10 @@ public override async Task ExecuteUpdate_works_for_table_sharing(bool async) AssertSql( """ +@p='1' + UPDATE "Vehicles" AS v -SET "SeatingCapacity" = 1 +SET "SeatingCapacity" = @p """, // """