diff --git a/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlStringMethodTranslator.cs b/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlStringMethodTranslator.cs index 7225cdb73c065e0a710219f0a9e12d7166f51f4d..96ebb80a6fd99e4d07a24a4c2573da4a18747dbd 100644 --- a/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlStringMethodTranslator.cs +++ b/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlStringMethodTranslator.cs @@ -41,7 +41,8 @@ public class NpgsqlStringMethodTranslator : IMethodCallTranslator [NotNull] static readonly MethodInfo TrimBothWithNoParam = typeof(string).GetRuntimeMethod(nameof(string.Trim), Type.EmptyTypes); [NotNull] static readonly MethodInfo TrimBothWithChars = typeof(string).GetRuntimeMethod(nameof(string.Trim), new[] { typeof(char[]) }); [NotNull] static readonly MethodInfo TrimBothWithSingleChar = typeof(string).GetRuntimeMethod(nameof(string.Trim), new[] { typeof(char) }); - [NotNull] static readonly MethodInfo Substring = typeof(string).GetTypeInfo().GetDeclaredMethods(nameof(string.Substring)).Single(m => m.GetParameters().Length == 2); + [NotNull] static readonly MethodInfo Substring = typeof(string).GetTypeInfo().GetDeclaredMethods(nameof(string.Substring)).Single(m => m.GetParameters().Length == 1); + [NotNull] static readonly MethodInfo SubstringWithLength = typeof(string).GetTypeInfo().GetDeclaredMethods(nameof(string.Substring)).Single(m => m.GetParameters().Length == 2); [NotNull] static readonly MethodInfo Replace = typeof(string).GetRuntimeMethod(nameof(string.Replace), new[] { typeof(string), typeof(string) }); [NotNull] static readonly MethodInfo PadLeft = typeof(string).GetRuntimeMethod(nameof(string.PadLeft), new[] { typeof(int) }); [NotNull] static readonly MethodInfo PadLeftWithChar = typeof(string).GetRuntimeMethod(nameof(string.PadLeft), new[] { typeof(int), typeof(char) }); @@ -106,16 +107,15 @@ public SqlExpression Translate(SqlExpression instance, MethodInfo method, IReadO instance.TypeMapping); } - if (method == Substring) + if (method == Substring || method == SubstringWithLength) { + var args = + method == Substring + ? new[] { instance, GenerateOneBasedIndexExpression(arguments[0]) } + : new[] { instance, GenerateOneBasedIndexExpression(arguments[0]), arguments[1] }; return _sqlExpressionFactory.Function( "SUBSTRING", - new[] - { - instance, - GenerateOneBasedIndexExpression(arguments[0]), - arguments[1] - }, + args, method.ReturnType, instance.TypeMapping); } diff --git a/test/EFCore.PG.FunctionalTests/Query/SimpleQueryNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/SimpleQueryNpgsqlTest.cs index 14cdae90b360e1d08a9e23242d2018608def94e0..5f2c2e2d7ad4325c9721e7ef378d5a42b3b055db 100644 --- a/test/EFCore.PG.FunctionalTests/Query/SimpleQueryNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/SimpleQueryNpgsqlTest.cs @@ -172,6 +172,35 @@ public Task PadRight_char_with_parameter(bool isAsync) #endregion + #region Substring + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public Task Substring_without_length_with_Index_of(bool isAsync) + => AssertQuery(isAsync, cs => cs + .Where(x => x.Address == "Walserweg 21") + .Where(x => x.Address.Substring(x.Address.IndexOf("e")) == "erweg 21"), entryCount: 1); + + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public Task Substring_without_length_with_constant(bool isAsync) + => AssertQuery(isAsync, cs => cs + //Walserweg 21 + .Where(x => x.Address.Substring(5) == "rweg 21"), entryCount: 1); + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public Task Substring_without_length_with_closure(bool isAsync) + { + var startIndex = 5; + return AssertQuery(isAsync, cs => cs + //Walserweg 21 + .Where(x => x.Address.Substring(startIndex) == "rweg 21"), entryCount: 1); + } + + #endregion + #region Array contains // Note that this also takes care of array.Any(x => x == y)