From 0d323c06d8da732c70751e5c60f9b60da6ce80d8 Mon Sep 17 00:00:00 2001 From: Roman Marusyk Date: Tue, 23 Feb 2021 23:08:28 +0200 Subject: [PATCH] Cosmos: Add translator for TrimStart/TrimEnd/Trim methods which map to LTRIM/RTRIM/TRIM built-in functions --- .../Query/Internal/StringMethodTranslator.cs | 44 ++++++++++++++++++- .../NorthwindFunctionsQueryCosmosTest.cs | 9 ++-- 2 files changed, 46 insertions(+), 7 deletions(-) diff --git a/src/EFCore.Cosmos/Query/Internal/StringMethodTranslator.cs b/src/EFCore.Cosmos/Query/Internal/StringMethodTranslator.cs index 9a7eab5e116..81b480ef0c8 100644 --- a/src/EFCore.Cosmos/Query/Internal/StringMethodTranslator.cs +++ b/src/EFCore.Cosmos/Query/Internal/StringMethodTranslator.cs @@ -36,6 +36,24 @@ private static readonly MethodInfo _toLowerMethodInfo private static readonly MethodInfo _toUpperMethodInfo = typeof(string).GetRequiredRuntimeMethod(nameof(string.ToUpper), Array.Empty()); + private static readonly MethodInfo _trimStartMethodInfoWithoutArgs + = typeof(string).GetRequiredRuntimeMethod(nameof(string.TrimStart), Array.Empty()); + + private static readonly MethodInfo _trimEndMethodInfoWithoutArgs + = typeof(string).GetRequiredRuntimeMethod(nameof(string.TrimEnd), Array.Empty()); + + private static readonly MethodInfo _trimMethodInfoWithoutArgs + = typeof(string).GetRequiredRuntimeMethod(nameof(string.Trim), Array.Empty()); + + private static readonly MethodInfo _trimStartMethodInfoWithCharArrayArg + = typeof(string).GetRequiredRuntimeMethod(nameof(string.TrimStart), new[] { typeof(char[]) }); + + private static readonly MethodInfo _trimEndMethodInfoWithCharArrayArg + = typeof(string).GetRequiredRuntimeMethod(nameof(string.TrimEnd), new[] { typeof(char[]) }); + + private static readonly MethodInfo _trimMethodInfoWithCharArrayArg + = typeof(string).GetRequiredRuntimeMethod(nameof(string.Trim), new[] { typeof(char[]) }); + private static readonly MethodInfo _firstOrDefaultMethodInfoWithoutArgs = typeof(Enumerable).GetRuntimeMethods().Single( m => m.Name == nameof(Enumerable.FirstOrDefault) @@ -108,11 +126,35 @@ public StringMethodTranslator([NotNull] ISqlExpressionFactory sqlExpressionFacto { return TranslateSystemFunction("LOWER", method.ReturnType, instance); } - + if (_toUpperMethodInfo.Equals(method)) { return TranslateSystemFunction("UPPER", method.ReturnType, instance); } + + if (_trimStartMethodInfoWithoutArgs?.Equals(method) == true + || (_trimStartMethodInfoWithCharArrayArg.Equals(method) + // Cosmos DB LTRIM does not take arguments + && ((arguments[0] as SqlConstantExpression)?.Value as Array)?.Length == 0)) + { + return TranslateSystemFunction("LTRIM", method.ReturnType, instance); + } + + if (_trimEndMethodInfoWithoutArgs?.Equals(method) == true + || (_trimEndMethodInfoWithCharArrayArg.Equals(method) + // Cosmos DB RTRIM does not take arguments + && ((arguments[0] as SqlConstantExpression)?.Value as Array)?.Length == 0)) + { + return TranslateSystemFunction("RTRIM", method.ReturnType, instance); + } + + if (_trimMethodInfoWithoutArgs?.Equals(method) == true + || (_trimMethodInfoWithCharArrayArg.Equals(method) + // Cosmos DB TRIM does not take arguments + && ((arguments[0] as SqlConstantExpression)?.Value as Array)?.Length == 0)) + { + return TranslateSystemFunction("TRIM", method.ReturnType, instance); + } } if (_firstOrDefaultMethodInfoWithoutArgs.Equals(method)) diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindFunctionsQueryCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindFunctionsQueryCosmosTest.cs index 4e249d0df3c..6fbc55eee35 100644 --- a/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindFunctionsQueryCosmosTest.cs +++ b/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindFunctionsQueryCosmosTest.cs @@ -843,7 +843,6 @@ FROM root c WHERE (c[""Discriminator""] = ""Customer"")"); } - [ConditionalTheory(Skip = "Issue #17246")] public override async Task TrimStart_without_arguments_in_predicate(bool async) { await base.TrimStart_without_arguments_in_predicate(async); @@ -851,7 +850,7 @@ public override async Task TrimStart_without_arguments_in_predicate(bool async) AssertSql( @"SELECT c FROM root c -WHERE (c[""Discriminator""] = ""Customer"")"); +WHERE ((c[""Discriminator""] = ""Customer"") AND (LTRIM(c[""ContactTitle""]) = ""Owner""))"); } [ConditionalTheory(Skip = "Issue #17246")] @@ -876,7 +875,6 @@ FROM root c WHERE (c[""Discriminator""] = ""Customer"")"); } - [ConditionalTheory(Skip = "Issue #17246")] public override async Task TrimEnd_without_arguments_in_predicate(bool async) { await base.TrimEnd_without_arguments_in_predicate(async); @@ -884,7 +882,7 @@ public override async Task TrimEnd_without_arguments_in_predicate(bool async) AssertSql( @"SELECT c FROM root c -WHERE (c[""Discriminator""] = ""Customer"")"); +WHERE ((c[""Discriminator""] = ""Customer"") AND (RTRIM(c[""ContactTitle""]) = ""Owner""))"); } [ConditionalTheory(Skip = "Issue #17246")] @@ -909,7 +907,6 @@ FROM root c WHERE (c[""Discriminator""] = ""Customer"")"); } - [ConditionalTheory(Skip = "Issue #17246")] public override async Task Trim_without_argument_in_predicate(bool async) { await base.Trim_without_argument_in_predicate(async); @@ -917,7 +914,7 @@ public override async Task Trim_without_argument_in_predicate(bool async) AssertSql( @"SELECT c FROM root c -WHERE (c[""Discriminator""] = ""Customer"")"); +WHERE ((c[""Discriminator""] = ""Customer"") AND (TRIM(c[""ContactTitle""]) = ""Owner""))"); } [ConditionalTheory(Skip = "Issue #17246")]