diff --git a/src/EFCore.SqlServer/Query/Internal/Translators/SqlServerByteArrayMethodTranslator.cs b/src/EFCore.SqlServer/Query/Internal/Translators/SqlServerByteArrayMethodTranslator.cs
index c50f5420e35..335b2b1c299 100644
--- a/src/EFCore.SqlServer/Query/Internal/Translators/SqlServerByteArrayMethodTranslator.cs
+++ b/src/EFCore.SqlServer/Query/Internal/Translators/SqlServerByteArrayMethodTranslator.cs
@@ -14,6 +14,12 @@ namespace Microsoft.EntityFrameworkCore.SqlServer.Query.Internal;
///
public class SqlServerByteArrayMethodTranslator : IMethodCallTranslator
{
+ private static readonly MethodInfo ArrayIndexOf
+ = typeof(Array).GetMethod(nameof(Array.IndexOf), 1, BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly, null, CallingConventions.Any, [Type.MakeGenericMethodParameter(0).MakeArrayType(), Type.MakeGenericMethodParameter(0)], null)!;
+
+ private static readonly MethodInfo ArrayIndexOfWithStartIndex
+ = typeof(Array).GetMethod(nameof(Array.IndexOf), 1, BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly, null, CallingConventions.Any, [Type.MakeGenericMethodParameter(0).MakeArrayType(), Type.MakeGenericMethodParameter(0), typeof(int)], null)!;
+
private readonly ISqlExpressionFactory _sqlExpressionFactory;
///
@@ -38,31 +44,32 @@ public SqlServerByteArrayMethodTranslator(ISqlExpressionFactory sqlExpressionFac
IDiagnosticsLogger logger)
{
if (method.IsGenericMethod
- && method.GetGenericMethodDefinition().Equals(EnumerableMethods.Contains)
+ && arguments.Count >= 1
&& arguments[0].Type == typeof(byte[]))
{
- var source = arguments[0];
- var sourceTypeMapping = source.TypeMapping;
+ var methodDefinition = method.GetGenericMethodDefinition();
+ if (methodDefinition.Equals(EnumerableMethods.Contains))
+ {
+ var source = arguments[0];
+ var sourceTypeMapping = source.TypeMapping;
- var value = arguments[1] is SqlConstantExpression constantValue
- ? _sqlExpressionFactory.Constant(new[] { (byte)constantValue.Value! }, sourceTypeMapping)
- : _sqlExpressionFactory.Convert(arguments[1], typeof(byte[]), sourceTypeMapping);
+ var value = arguments[1] is SqlConstantExpression constantValue
+ ? _sqlExpressionFactory.Constant(new[] { (byte)constantValue.Value! }, sourceTypeMapping)
+ : _sqlExpressionFactory.Convert(arguments[1], typeof(byte[]), sourceTypeMapping);
- return _sqlExpressionFactory.GreaterThan(
- _sqlExpressionFactory.Function(
- "CHARINDEX",
- [value, source],
- nullable: true,
- argumentsPropagateNullability: [true, true],
- typeof(int)),
- _sqlExpressionFactory.Constant(0));
- }
+ return _sqlExpressionFactory.GreaterThan(
+ _sqlExpressionFactory.Function(
+ "CHARINDEX",
+ [value, source],
+ nullable: true,
+ argumentsPropagateNullability: [true, true],
+ typeof(int)),
+ _sqlExpressionFactory.Constant(0));
+ }
- if (method.IsGenericMethod
- && method.GetGenericMethodDefinition().Equals(EnumerableMethods.FirstWithoutPredicate)
- && arguments[0].Type == typeof(byte[]))
- {
- return _sqlExpressionFactory.Convert(
+ if (methodDefinition.Equals(EnumerableMethods.FirstWithoutPredicate))
+ {
+ return _sqlExpressionFactory.Convert(
_sqlExpressionFactory.Function(
"SUBSTRING",
[arguments[0], _sqlExpressionFactory.Constant(1), _sqlExpressionFactory.Constant(1)],
@@ -70,8 +77,69 @@ public SqlServerByteArrayMethodTranslator(ISqlExpressionFactory sqlExpressionFac
argumentsPropagateNullability: [true, true, true],
typeof(byte[])),
method.ReturnType);
+ }
+
+ if (methodDefinition.Equals(ArrayIndexOf))
+ {
+ return TranslateByteArrayIndexOf(method, arguments[0], arguments[1], null);
+ }
+
+ if (methodDefinition.Equals(ArrayIndexOfWithStartIndex))
+ {
+ return TranslateByteArrayIndexOf(method, arguments[0], arguments[1], arguments[2]);
+ }
}
return null;
}
+
+ private SqlExpression TranslateByteArrayIndexOf(
+ MethodInfo method,
+ SqlExpression source,
+ SqlExpression valueToSearch,
+ SqlExpression? startIndex)
+ {
+ var sourceTypeMapping = source.TypeMapping;
+ var sqlArguments = new List
+ {
+ valueToSearch is SqlConstantExpression { Value: byte constantValue }
+ ? _sqlExpressionFactory.Constant(new byte[] { constantValue }, sourceTypeMapping)
+ : _sqlExpressionFactory.Convert(valueToSearch, typeof(byte[]), sourceTypeMapping),
+ source
+ };
+
+ if (startIndex is not null)
+ {
+ sqlArguments.Add(
+ startIndex is SqlConstantExpression { Value : int index }
+ ? _sqlExpressionFactory.Constant(index + 1, typeof(int))
+ : _sqlExpressionFactory.Add(startIndex, _sqlExpressionFactory.Constant(1)));
+ }
+
+ var argumentsPropagateNullability = Enumerable.Repeat(true, sqlArguments.Count);
+
+ SqlExpression charIndexExpr;
+ var storeType = sourceTypeMapping?.StoreType;
+ if (storeType == "varbinary(max)")
+ {
+ charIndexExpr = GetCharIndexSqlFunctionExpression(sqlArguments, argumentsPropagateNullability, typeof(long));
+ charIndexExpr = _sqlExpressionFactory.Convert(charIndexExpr, typeof(int));
+ }
+ else
+ {
+ charIndexExpr = GetCharIndexSqlFunctionExpression(sqlArguments, argumentsPropagateNullability, method.ReturnType);
+ }
+
+ return _sqlExpressionFactory.Subtract(charIndexExpr, _sqlExpressionFactory.Constant(1));
+
+ SqlExpression GetCharIndexSqlFunctionExpression(List sqlArguments, IEnumerable argumentsPropagateNullability, Type returnType)
+ {
+ return _sqlExpressionFactory.Function(
+ "CHARINDEX",
+ sqlArguments,
+ nullable: true,
+ argumentsPropagateNullability: argumentsPropagateNullability,
+ returnType);
+ }
+ }
}
diff --git a/src/EFCore.Sqlite.Core/Query/Internal/Translators/SqliteByteArrayMethodTranslator.cs b/src/EFCore.Sqlite.Core/Query/Internal/Translators/SqliteByteArrayMethodTranslator.cs
index 195726644e3..2a5becdbae9 100644
--- a/src/EFCore.Sqlite.Core/Query/Internal/Translators/SqliteByteArrayMethodTranslator.cs
+++ b/src/EFCore.Sqlite.Core/Query/Internal/Translators/SqliteByteArrayMethodTranslator.cs
@@ -14,6 +14,9 @@ namespace Microsoft.EntityFrameworkCore.Sqlite.Query.Internal;
///
public class SqliteByteArrayMethodTranslator : IMethodCallTranslator
{
+ private static readonly MethodInfo ArrayIndexOf
+ = typeof(Array).GetMethod(nameof(Array.IndexOf), 1, BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly, null, CallingConventions.Any, [Type.MakeGenericMethodParameter(0).MakeArrayType(), Type.MakeGenericMethodParameter(0)], null)!;
+
private readonly ISqlExpressionFactory _sqlExpressionFactory;
///
@@ -38,28 +41,26 @@ public SqliteByteArrayMethodTranslator(ISqlExpressionFactory sqlExpressionFactor
IDiagnosticsLogger logger)
{
if (method.IsGenericMethod
- && method.GetGenericMethodDefinition().Equals(EnumerableMethods.Contains)
+ && arguments.Count >= 1
&& arguments[0].Type == typeof(byte[]))
{
- var source = arguments[0];
+ var genericMethodDefinition = method.GetGenericMethodDefinition();
+ if (genericMethodDefinition.Equals(EnumerableMethods.Contains))
+ {
+ return _sqlExpressionFactory.GreaterThan(
+ GetInStrSqlFunctionExpression(arguments[0], arguments[1]),
+ _sqlExpressionFactory.Constant(0));
- var value = arguments[1] is SqlConstantExpression constantValue
- ? (SqlExpression)_sqlExpressionFactory.Constant(new[] { (byte)constantValue.Value! }, source.TypeMapping)
- : _sqlExpressionFactory.Function(
- "char",
- new[] { arguments[1] },
- nullable: false,
- argumentsPropagateNullability: new[] { false },
- typeof(string));
+ }
+
+ if (genericMethodDefinition.Equals(ArrayIndexOf))
+ {
+ return _sqlExpressionFactory.Subtract(
+ GetInStrSqlFunctionExpression(arguments[0], arguments[1]),
+ _sqlExpressionFactory.Constant(1));
+ }
- return _sqlExpressionFactory.GreaterThan(
- _sqlExpressionFactory.Function(
- "instr",
- new[] { source, value },
- nullable: true,
- argumentsPropagateNullability: new[] { true, true },
- typeof(int)),
- _sqlExpressionFactory.Constant(0));
+ // NOTE: IndexOf Method with a starting position is not supported by SQLite
}
// See issue#16428
@@ -89,5 +90,24 @@ public SqliteByteArrayMethodTranslator(ISqlExpressionFactory sqlExpressionFactor
//}
return null;
+
+ SqlExpression GetInStrSqlFunctionExpression(SqlExpression source, SqlExpression valueToSearch)
+ {
+ var value = valueToSearch is SqlConstantExpression { Value: byte constantValue }
+ ? _sqlExpressionFactory.Constant(new byte[] { constantValue }, source.TypeMapping)
+ : _sqlExpressionFactory.Function(
+ "char",
+ [valueToSearch],
+ nullable: false,
+ argumentsPropagateNullability: [false],
+ typeof(string));
+
+ return _sqlExpressionFactory.Function(
+ "instr",
+ [source, value],
+ nullable: true,
+ argumentsPropagateNullability: [true, true],
+ typeof(int));
+ }
}
}
diff --git a/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs
index ec8180be84c..dcc7b7c2bce 100644
--- a/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs
+++ b/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs
@@ -6258,6 +6258,88 @@ public virtual Task Byte_array_filter_by_length_parameter(bool async)
ss => ss.Set().Where(w => w.Banner != null && w.Banner.Length == someByteArr.Length));
}
+ #region Byte Array IndexOf
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Byte_array_IndexOf_with_literal(bool async)
+ => AssertQuery(
+ async,
+ ss => ss.Set().Where(w => Array.IndexOf(w.Banner, (byte)1) == 1),
+ ss => ss.Set().Where(w => w.Banner != null && Array.IndexOf(w.Banner, (byte)1) == 1));
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Byte_array_IndexOf_with_parameter(bool async)
+ {
+ byte b = 0;
+ return AssertQuery(
+ async,
+ ss => ss.Set().Where(w => Array.IndexOf(w.Banner, b) == 0),
+ ss => ss.Set().Where(w => w.Banner != null && Array.IndexOf(w.Banner, b) == 0));
+ }
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Byte_array_with_length_IndexOf_with_literal(bool async)
+ => AssertQuery(
+ async,
+ ss => ss.Set().Where(w => Array.IndexOf(w.Banner5, (byte)5) == 1),
+ ss => ss.Set().Where(w => w.Banner != null && Array.IndexOf(w.Banner5, (byte)5) == 1));
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Byte_array_with_length_IndexOf_with_parameter(bool async)
+ {
+ byte b = 4;
+ return AssertQuery(
+ async,
+ ss => ss.Set().Where(w => Array.IndexOf(w.Banner5, b) == 0),
+ ss => ss.Set().Where(w => w.Banner != null && Array.IndexOf(w.Banner5, b) == 0));
+ }
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Byte_array_IndexOf_with_startIndex_with_literals(bool async)
+ => AssertQuery(
+ async,
+ ss => ss.Set().Where(w => Array.IndexOf(w.Banner, (byte)1, 1) == 1),
+ ss => ss.Set().Where(w => w.Banner != null && Array.IndexOf(w.Banner, (byte)1, 1) == 1));
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Byte_array_IndexOf_with_startIndex_with_parameters(bool async)
+ {
+ byte b = 0;
+ var startPos = 0;
+ return AssertQuery(
+ async,
+ ss => ss.Set().Where(w => Array.IndexOf(w.Banner, b, startPos) == 0),
+ ss => ss.Set().Where(w => w.Banner != null && Array.IndexOf(w.Banner, b, startPos) == 0));
+ }
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Byte_array_with_length_IndexOf_with_startIndex_with_literals(bool async)
+ => AssertQuery(
+ async,
+ ss => ss.Set().Where(w => Array.IndexOf(w.Banner5, (byte)5, 1) == 1),
+ ss => ss.Set().Where(w => w.Banner != null && Array.IndexOf(w.Banner5, (byte)5, 1) == 1));
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Byte_array_with_length_IndexOf_with_startIndex_with_parameters(bool async)
+ {
+ byte b = 4;
+ var startPos = 0;
+ return AssertQuery(
+ async,
+ ss => ss.Set().Where(w => Array.IndexOf(w.Banner5, b, startPos) == 0),
+ ss => ss.Set().Where(w => w.Banner != null && Array.IndexOf(w.Banner5, b, startPos) == 0));
+ }
+
+ #endregion
+
[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task OrderBy_bool_coming_from_optional_navigation(bool async)
diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs
index 14db73c59cb..5185ccf7ed8 100644
--- a/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs
@@ -7610,6 +7610,20 @@ WHERE CHARINDEX(0x01, [s].[Banner]) > 0
""");
}
+ public override async Task Byte_array_contains_parameter(bool async)
+ {
+ await base.Byte_array_contains_parameter(async);
+
+ AssertSql(
+ """
+@__someByte_0='1' (Size = 1)
+
+SELECT [s].[Id], [s].[Banner], [s].[Banner5], [s].[InternalNumber], [s].[Name]
+FROM [Squads] AS [s]
+WHERE CHARINDEX(CAST(@__someByte_0 AS varbinary(max)), [s].[Banner]) > 0
+""");
+ }
+
public override async Task Byte_array_filter_by_length_literal(bool async)
{
await base.Byte_array_filter_by_length_literal(async);
@@ -7650,32 +7664,128 @@ WHERE CAST(DATALENGTH([s].[Banner]) AS int) = CAST(DATALENGTH(@__byteArrayParam)
""");
}
- public override async Task Byte_array_contains_parameter(bool async)
+ public override async Task Byte_array_filter_by_length_literal_does_not_cast_on_varbinary_n(bool async)
{
- await base.Byte_array_contains_parameter(async);
+ await base.Byte_array_filter_by_length_literal_does_not_cast_on_varbinary_n(async);
AssertSql(
"""
-@__someByte_0='1' (Size = 1)
+SELECT [s].[Id], [s].[Banner], [s].[Banner5], [s].[InternalNumber], [s].[Name]
+FROM [Squads] AS [s]
+WHERE DATALENGTH([s].[Banner5]) = 5
+""");
+ }
+
+ #region Byte Array IndexOf Translation
+
+ public override async Task Byte_array_IndexOf_with_literal(bool async)
+ {
+ await base.Byte_array_IndexOf_with_literal(async);
+ AssertSql(
+ """
SELECT [s].[Id], [s].[Banner], [s].[Banner5], [s].[InternalNumber], [s].[Name]
FROM [Squads] AS [s]
-WHERE CHARINDEX(CAST(@__someByte_0 AS varbinary(max)), [s].[Banner]) > 0
+WHERE CAST(CHARINDEX(0x01, [s].[Banner]) AS int) - 1 = 1
""");
}
- public override async Task Byte_array_filter_by_length_literal_does_not_cast_on_varbinary_n(bool async)
+ public override async Task Byte_array_IndexOf_with_parameter(bool async)
{
- await base.Byte_array_filter_by_length_literal_does_not_cast_on_varbinary_n(async);
+ await base.Byte_array_IndexOf_with_parameter(async);
AssertSql(
"""
+@__b_0='0' (Size = 1)
+
SELECT [s].[Id], [s].[Banner], [s].[Banner5], [s].[InternalNumber], [s].[Name]
FROM [Squads] AS [s]
-WHERE DATALENGTH([s].[Banner5]) = 5
+WHERE CAST(CHARINDEX(CAST(@__b_0 AS varbinary(max)), [s].[Banner]) AS int) - 1 = 0
+""");
+ }
+
+ public override async Task Byte_array_with_length_IndexOf_with_literal(bool async)
+ {
+ await base.Byte_array_with_length_IndexOf_with_literal(async);
+
+ AssertSql(
+ """
+SELECT [s].[Id], [s].[Banner], [s].[Banner5], [s].[InternalNumber], [s].[Name]
+FROM [Squads] AS [s]
+WHERE CHARINDEX(0x05, [s].[Banner5]) - 1 = 1
+""");
+ }
+
+ public override async Task Byte_array_with_length_IndexOf_with_parameter(bool async)
+ {
+ await base.Byte_array_with_length_IndexOf_with_parameter(async);
+
+ AssertSql(
+ """
+@__b_0='4' (Size = 1)
+
+SELECT [s].[Id], [s].[Banner], [s].[Banner5], [s].[InternalNumber], [s].[Name]
+FROM [Squads] AS [s]
+WHERE CHARINDEX(CAST(@__b_0 AS varbinary(5)), [s].[Banner5]) - 1 = 0
+""");
+ }
+
+ public override async Task Byte_array_IndexOf_with_startIndex_with_literals(bool async)
+ {
+ await base.Byte_array_IndexOf_with_startIndex_with_literals(async);
+
+ AssertSql(
+ """
+SELECT [s].[Id], [s].[Banner], [s].[Banner5], [s].[InternalNumber], [s].[Name]
+FROM [Squads] AS [s]
+WHERE CAST(CHARINDEX(0x01, [s].[Banner], 2) AS int) - 1 = 1
+""");
+ }
+
+ public override async Task Byte_array_IndexOf_with_startIndex_with_parameters(bool async)
+ {
+ await base.Byte_array_IndexOf_with_startIndex_with_parameters(async);
+
+ AssertSql(
+ """
+@__b_0='0' (Size = 1)
+@__startPos_1='0'
+
+SELECT [s].[Id], [s].[Banner], [s].[Banner5], [s].[InternalNumber], [s].[Name]
+FROM [Squads] AS [s]
+WHERE CAST(CHARINDEX(CAST(@__b_0 AS varbinary(max)), [s].[Banner], @__startPos_1 + 1) AS int) - 1 = 0
+""");
+ }
+
+ public override async Task Byte_array_with_length_IndexOf_with_startIndex_with_literals(bool async)
+ {
+ await base.Byte_array_with_length_IndexOf_with_startIndex_with_literals(async);
+
+ AssertSql(
+ """
+SELECT [s].[Id], [s].[Banner], [s].[Banner5], [s].[InternalNumber], [s].[Name]
+FROM [Squads] AS [s]
+WHERE CHARINDEX(0x05, [s].[Banner5], 2) - 1 = 1
""");
}
+ public override async Task Byte_array_with_length_IndexOf_with_startIndex_with_parameters(bool async)
+ {
+ await base.Byte_array_with_length_IndexOf_with_startIndex_with_parameters(async);
+
+ AssertSql(
+ """
+@__b_0='4' (Size = 1)
+@__startPos_1='0'
+
+SELECT [s].[Id], [s].[Banner], [s].[Banner5], [s].[InternalNumber], [s].[Name]
+FROM [Squads] AS [s]
+WHERE CHARINDEX(CAST(@__b_0 AS varbinary(5)), [s].[Banner5], @__startPos_1 + 1) - 1 = 0
+""");
+ }
+
+ #endregion
+
public override async Task Conditional_expression_with_test_being_simplified_to_constant_simple(bool isAsync)
{
await base.Conditional_expression_with_test_being_simplified_to_constant_simple(isAsync);
diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/TPCGearsOfWarQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/TPCGearsOfWarQuerySqlServerTest.cs
index f8d41c87f63..cac26b7d022 100644
--- a/test/EFCore.SqlServer.FunctionalTests/Query/TPCGearsOfWarQuerySqlServerTest.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/Query/TPCGearsOfWarQuerySqlServerTest.cs
@@ -10220,6 +10220,116 @@ WHERE DATALENGTH([s].[Banner5]) = 5
""");
}
+ #region Byte Array IndexOf Translation
+
+ public override async Task Byte_array_IndexOf_with_literal(bool async)
+ {
+ await base.Byte_array_IndexOf_with_literal(async);
+
+ AssertSql(
+ """
+SELECT [s].[Id], [s].[Banner], [s].[Banner5], [s].[InternalNumber], [s].[Name]
+FROM [Squads] AS [s]
+WHERE CAST(CHARINDEX(0x01, [s].[Banner]) AS int) - 1 = 1
+""");
+ }
+
+ public override async Task Byte_array_IndexOf_with_parameter(bool async)
+ {
+ await base.Byte_array_IndexOf_with_parameter(async);
+
+ AssertSql(
+ """
+@__b_0='0' (Size = 1)
+
+SELECT [s].[Id], [s].[Banner], [s].[Banner5], [s].[InternalNumber], [s].[Name]
+FROM [Squads] AS [s]
+WHERE CAST(CHARINDEX(CAST(@__b_0 AS varbinary(max)), [s].[Banner]) AS int) - 1 = 0
+""");
+ }
+
+ public override async Task Byte_array_with_length_IndexOf_with_literal(bool async)
+ {
+ await base.Byte_array_with_length_IndexOf_with_literal(async);
+
+ AssertSql(
+ """
+SELECT [s].[Id], [s].[Banner], [s].[Banner5], [s].[InternalNumber], [s].[Name]
+FROM [Squads] AS [s]
+WHERE CHARINDEX(0x05, [s].[Banner5]) - 1 = 1
+""");
+ }
+
+ public override async Task Byte_array_with_length_IndexOf_with_parameter(bool async)
+ {
+ await base.Byte_array_with_length_IndexOf_with_parameter(async);
+
+ AssertSql(
+ """
+@__b_0='4' (Size = 1)
+
+SELECT [s].[Id], [s].[Banner], [s].[Banner5], [s].[InternalNumber], [s].[Name]
+FROM [Squads] AS [s]
+WHERE CHARINDEX(CAST(@__b_0 AS varbinary(5)), [s].[Banner5]) - 1 = 0
+""");
+ }
+
+ public override async Task Byte_array_IndexOf_with_startIndex_with_literals(bool async)
+ {
+ await base.Byte_array_IndexOf_with_startIndex_with_literals(async);
+
+ AssertSql(
+ """
+SELECT [s].[Id], [s].[Banner], [s].[Banner5], [s].[InternalNumber], [s].[Name]
+FROM [Squads] AS [s]
+WHERE CAST(CHARINDEX(0x01, [s].[Banner], 2) AS int) - 1 = 1
+""");
+ }
+
+ public override async Task Byte_array_IndexOf_with_startIndex_with_parameters(bool async)
+ {
+ await base.Byte_array_IndexOf_with_startIndex_with_parameters(async);
+
+ AssertSql(
+ """
+@__b_0='0' (Size = 1)
+@__startPos_1='0'
+
+SELECT [s].[Id], [s].[Banner], [s].[Banner5], [s].[InternalNumber], [s].[Name]
+FROM [Squads] AS [s]
+WHERE CAST(CHARINDEX(CAST(@__b_0 AS varbinary(max)), [s].[Banner], @__startPos_1 + 1) AS int) - 1 = 0
+""");
+ }
+
+ public override async Task Byte_array_with_length_IndexOf_with_startIndex_with_literals(bool async)
+ {
+ await base.Byte_array_with_length_IndexOf_with_startIndex_with_literals(async);
+
+ AssertSql(
+ """
+SELECT [s].[Id], [s].[Banner], [s].[Banner5], [s].[InternalNumber], [s].[Name]
+FROM [Squads] AS [s]
+WHERE CHARINDEX(0x05, [s].[Banner5], 2) - 1 = 1
+""");
+ }
+
+ public override async Task Byte_array_with_length_IndexOf_with_startIndex_with_parameters(bool async)
+ {
+ await base.Byte_array_with_length_IndexOf_with_startIndex_with_parameters(async);
+
+ AssertSql(
+ """
+@__b_0='4' (Size = 1)
+@__startPos_1='0'
+
+SELECT [s].[Id], [s].[Banner], [s].[Banner5], [s].[InternalNumber], [s].[Name]
+FROM [Squads] AS [s]
+WHERE CHARINDEX(CAST(@__b_0 AS varbinary(5)), [s].[Banner5], @__startPos_1 + 1) - 1 = 0
+""");
+ }
+
+ #endregion
+
public override async Task Conditional_expression_with_test_being_simplified_to_constant_simple(bool isAsync)
{
await base.Conditional_expression_with_test_being_simplified_to_constant_simple(isAsync);
diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/TPTGearsOfWarQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/TPTGearsOfWarQuerySqlServerTest.cs
index e2260d6cc08..46557412d91 100644
--- a/test/EFCore.SqlServer.FunctionalTests/Query/TPTGearsOfWarQuerySqlServerTest.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/Query/TPTGearsOfWarQuerySqlServerTest.cs
@@ -8686,6 +8686,116 @@ WHERE DATALENGTH([s].[Banner5]) = 5
""");
}
+ #region Byte Array IndexOf Translation
+
+ public override async Task Byte_array_IndexOf_with_literal(bool async)
+ {
+ await base.Byte_array_IndexOf_with_literal(async);
+
+ AssertSql(
+ """
+SELECT [s].[Id], [s].[Banner], [s].[Banner5], [s].[InternalNumber], [s].[Name]
+FROM [Squads] AS [s]
+WHERE CAST(CHARINDEX(0x01, [s].[Banner]) AS int) - 1 = 1
+""");
+ }
+
+ public override async Task Byte_array_IndexOf_with_parameter(bool async)
+ {
+ await base.Byte_array_IndexOf_with_parameter(async);
+
+ AssertSql(
+ """
+@__b_0='0' (Size = 1)
+
+SELECT [s].[Id], [s].[Banner], [s].[Banner5], [s].[InternalNumber], [s].[Name]
+FROM [Squads] AS [s]
+WHERE CAST(CHARINDEX(CAST(@__b_0 AS varbinary(max)), [s].[Banner]) AS int) - 1 = 0
+""");
+ }
+
+ public override async Task Byte_array_with_length_IndexOf_with_literal(bool async)
+ {
+ await base.Byte_array_with_length_IndexOf_with_literal(async);
+
+ AssertSql(
+ """
+SELECT [s].[Id], [s].[Banner], [s].[Banner5], [s].[InternalNumber], [s].[Name]
+FROM [Squads] AS [s]
+WHERE CHARINDEX(0x05, [s].[Banner5]) - 1 = 1
+""");
+ }
+
+ public override async Task Byte_array_with_length_IndexOf_with_parameter(bool async)
+ {
+ await base.Byte_array_with_length_IndexOf_with_parameter(async);
+
+ AssertSql(
+ """
+@__b_0='4' (Size = 1)
+
+SELECT [s].[Id], [s].[Banner], [s].[Banner5], [s].[InternalNumber], [s].[Name]
+FROM [Squads] AS [s]
+WHERE CHARINDEX(CAST(@__b_0 AS varbinary(5)), [s].[Banner5]) - 1 = 0
+""");
+ }
+
+ public override async Task Byte_array_IndexOf_with_startIndex_with_literals(bool async)
+ {
+ await base.Byte_array_IndexOf_with_startIndex_with_literals(async);
+
+ AssertSql(
+ """
+SELECT [s].[Id], [s].[Banner], [s].[Banner5], [s].[InternalNumber], [s].[Name]
+FROM [Squads] AS [s]
+WHERE CAST(CHARINDEX(0x01, [s].[Banner], 2) AS int) - 1 = 1
+""");
+ }
+
+ public override async Task Byte_array_IndexOf_with_startIndex_with_parameters(bool async)
+ {
+ await base.Byte_array_IndexOf_with_startIndex_with_parameters(async);
+
+ AssertSql(
+ """
+@__b_0='0' (Size = 1)
+@__startPos_1='0'
+
+SELECT [s].[Id], [s].[Banner], [s].[Banner5], [s].[InternalNumber], [s].[Name]
+FROM [Squads] AS [s]
+WHERE CAST(CHARINDEX(CAST(@__b_0 AS varbinary(max)), [s].[Banner], @__startPos_1 + 1) AS int) - 1 = 0
+""");
+ }
+
+ public override async Task Byte_array_with_length_IndexOf_with_startIndex_with_literals(bool async)
+ {
+ await base.Byte_array_with_length_IndexOf_with_startIndex_with_literals(async);
+
+ AssertSql(
+ """
+SELECT [s].[Id], [s].[Banner], [s].[Banner5], [s].[InternalNumber], [s].[Name]
+FROM [Squads] AS [s]
+WHERE CHARINDEX(0x05, [s].[Banner5], 2) - 1 = 1
+""");
+ }
+
+ public override async Task Byte_array_with_length_IndexOf_with_startIndex_with_parameters(bool async)
+ {
+ await base.Byte_array_with_length_IndexOf_with_startIndex_with_parameters(async);
+
+ AssertSql(
+ """
+@__b_0='4' (Size = 1)
+@__startPos_1='0'
+
+SELECT [s].[Id], [s].[Banner], [s].[Banner5], [s].[InternalNumber], [s].[Name]
+FROM [Squads] AS [s]
+WHERE CHARINDEX(CAST(@__b_0 AS varbinary(5)), [s].[Banner5], @__startPos_1 + 1) - 1 = 0
+""");
+ }
+
+ #endregion
+
public override async Task Conditional_expression_with_test_being_simplified_to_constant_simple(bool isAsync)
{
await base.Conditional_expression_with_test_being_simplified_to_constant_simple(isAsync);
diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/TemporalGearsOfWarQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/TemporalGearsOfWarQuerySqlServerTest.cs
index 6895ee561b1..cef85fd94b6 100644
--- a/test/EFCore.SqlServer.FunctionalTests/Query/TemporalGearsOfWarQuerySqlServerTest.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/Query/TemporalGearsOfWarQuerySqlServerTest.cs
@@ -3262,6 +3262,116 @@ WHEN [t].[GearNickName] IS NOT NULL THEN [g].[Nickname]
""");
}
+ #region Byte Array IndexOf Translation
+
+ public override async Task Byte_array_IndexOf_with_literal(bool async)
+ {
+ await base.Byte_array_IndexOf_with_literal(async);
+
+ AssertSql(
+ """
+SELECT [s].[Id], [s].[Banner], [s].[Banner5], [s].[InternalNumber], [s].[Name], [s].[PeriodEnd], [s].[PeriodStart]
+FROM [Squads] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [s]
+WHERE CAST(CHARINDEX(0x01, [s].[Banner]) AS int) - 1 = 1
+""");
+ }
+
+ public override async Task Byte_array_IndexOf_with_parameter(bool async)
+ {
+ await base.Byte_array_IndexOf_with_parameter(async);
+
+ AssertSql(
+ """
+@__b_0='0' (Size = 1)
+
+SELECT [s].[Id], [s].[Banner], [s].[Banner5], [s].[InternalNumber], [s].[Name], [s].[PeriodEnd], [s].[PeriodStart]
+FROM [Squads] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [s]
+WHERE CAST(CHARINDEX(CAST(@__b_0 AS varbinary(max)), [s].[Banner]) AS int) - 1 = 0
+""");
+ }
+
+ public override async Task Byte_array_with_length_IndexOf_with_literal(bool async)
+ {
+ await base.Byte_array_with_length_IndexOf_with_literal(async);
+
+ AssertSql(
+ """
+SELECT [s].[Id], [s].[Banner], [s].[Banner5], [s].[InternalNumber], [s].[Name], [s].[PeriodEnd], [s].[PeriodStart]
+FROM [Squads] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [s]
+WHERE CHARINDEX(0x05, [s].[Banner5]) - 1 = 1
+""");
+ }
+
+ public override async Task Byte_array_with_length_IndexOf_with_parameter(bool async)
+ {
+ await base.Byte_array_with_length_IndexOf_with_parameter(async);
+
+ AssertSql(
+ """
+@__b_0='4' (Size = 1)
+
+SELECT [s].[Id], [s].[Banner], [s].[Banner5], [s].[InternalNumber], [s].[Name], [s].[PeriodEnd], [s].[PeriodStart]
+FROM [Squads] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [s]
+WHERE CHARINDEX(CAST(@__b_0 AS varbinary(5)), [s].[Banner5]) - 1 = 0
+""");
+ }
+
+ public override async Task Byte_array_IndexOf_with_startIndex_with_literals(bool async)
+ {
+ await base.Byte_array_IndexOf_with_startIndex_with_literals(async);
+
+ AssertSql(
+ """
+SELECT [s].[Id], [s].[Banner], [s].[Banner5], [s].[InternalNumber], [s].[Name], [s].[PeriodEnd], [s].[PeriodStart]
+FROM [Squads] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [s]
+WHERE CAST(CHARINDEX(0x01, [s].[Banner], 2) AS int) - 1 = 1
+""");
+ }
+
+ public override async Task Byte_array_IndexOf_with_startIndex_with_parameters(bool async)
+ {
+ await base.Byte_array_IndexOf_with_startIndex_with_parameters(async);
+
+ AssertSql(
+ """
+@__b_0='0' (Size = 1)
+@__startPos_1='0'
+
+SELECT [s].[Id], [s].[Banner], [s].[Banner5], [s].[InternalNumber], [s].[Name], [s].[PeriodEnd], [s].[PeriodStart]
+FROM [Squads] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [s]
+WHERE CAST(CHARINDEX(CAST(@__b_0 AS varbinary(max)), [s].[Banner], @__startPos_1 + 1) AS int) - 1 = 0
+""");
+ }
+
+ public override async Task Byte_array_with_length_IndexOf_with_startIndex_with_literals(bool async)
+ {
+ await base.Byte_array_with_length_IndexOf_with_startIndex_with_literals(async);
+
+ AssertSql(
+ """
+SELECT [s].[Id], [s].[Banner], [s].[Banner5], [s].[InternalNumber], [s].[Name], [s].[PeriodEnd], [s].[PeriodStart]
+FROM [Squads] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [s]
+WHERE CHARINDEX(0x05, [s].[Banner5], 2) - 1 = 1
+""");
+ }
+
+ public override async Task Byte_array_with_length_IndexOf_with_startIndex_with_parameters(bool async)
+ {
+ await base.Byte_array_with_length_IndexOf_with_startIndex_with_parameters(async);
+
+ AssertSql(
+ """
+@__b_0='4' (Size = 1)
+@__startPos_1='0'
+
+SELECT [s].[Id], [s].[Banner], [s].[Banner5], [s].[InternalNumber], [s].[Name], [s].[PeriodEnd], [s].[PeriodStart]
+FROM [Squads] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [s]
+WHERE CHARINDEX(CAST(@__b_0 AS varbinary(5)), [s].[Banner5], @__startPos_1 + 1) - 1 = 0
+""");
+ }
+
+ #endregion
+
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);
diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/GearsOfWarQuerySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/GearsOfWarQuerySqliteTest.cs
index 6fff187cf93..194d99a922a 100644
--- a/test/EFCore.Sqlite.FunctionalTests/Query/GearsOfWarQuerySqliteTest.cs
+++ b/test/EFCore.Sqlite.FunctionalTests/Query/GearsOfWarQuerySqliteTest.cs
@@ -436,6 +436,74 @@ WHERE length("s"."Banner") = length(@__byteArrayParam)
""");
}
+ #region Byte Array IndexOf Translation
+
+ public override async Task Byte_array_IndexOf_with_literal(bool async)
+ {
+ await base.Byte_array_IndexOf_with_literal(async);
+
+ AssertSql(
+ """
+SELECT "s"."Id", "s"."Banner", "s"."Banner5", "s"."InternalNumber", "s"."Name"
+FROM "Squads" AS "s"
+WHERE instr("s"."Banner", X'01') - 1 = 1
+""");
+ }
+
+ public override async Task Byte_array_IndexOf_with_parameter(bool async)
+ {
+ await base.Byte_array_IndexOf_with_parameter(async);
+
+ AssertSql(
+ """
+@__b_0='0'
+
+SELECT "s"."Id", "s"."Banner", "s"."Banner5", "s"."InternalNumber", "s"."Name"
+FROM "Squads" AS "s"
+WHERE instr("s"."Banner", char(@__b_0)) - 1 = 0
+""");
+ }
+
+ public override async Task Byte_array_with_length_IndexOf_with_literal(bool async)
+ {
+ await base.Byte_array_with_length_IndexOf_with_literal(async);
+
+ AssertSql(
+ """
+SELECT "s"."Id", "s"."Banner", "s"."Banner5", "s"."InternalNumber", "s"."Name"
+FROM "Squads" AS "s"
+WHERE instr("s"."Banner5", X'05') - 1 = 1
+""");
+ }
+
+ public override async Task Byte_array_with_length_IndexOf_with_parameter(bool async)
+ {
+ await base.Byte_array_with_length_IndexOf_with_parameter(async);
+
+ AssertSql(
+ """
+@__b_0='4'
+
+SELECT "s"."Id", "s"."Banner", "s"."Banner5", "s"."InternalNumber", "s"."Name"
+FROM "Squads" AS "s"
+WHERE instr("s"."Banner5", char(@__b_0)) - 1 = 0
+""");
+ }
+
+ public override Task Byte_array_IndexOf_with_startIndex_with_literals(bool async)
+ => AssertTranslationFailed(() => base.Byte_array_IndexOf_with_startIndex_with_literals(async));
+
+ public override Task Byte_array_IndexOf_with_startIndex_with_parameters(bool async)
+ => AssertTranslationFailed(() => base.Byte_array_IndexOf_with_startIndex_with_parameters(async));
+
+ public override Task Byte_array_with_length_IndexOf_with_startIndex_with_literals(bool async)
+ => AssertTranslationFailed(() => base.Byte_array_with_length_IndexOf_with_startIndex_with_literals(async));
+
+ public override Task Byte_array_with_length_IndexOf_with_startIndex_with_parameters(bool async)
+ => AssertTranslationFailed(() => base.Byte_array_with_length_IndexOf_with_startIndex_with_parameters(async));
+
+ #endregion
+
public override async Task Byte_array_filter_by_SequenceEqual(bool async)
{
await base.Byte_array_filter_by_SequenceEqual(async);
diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/TPCGearsOfWarQuerySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/TPCGearsOfWarQuerySqliteTest.cs
index 07174a52b7b..a5527586844 100644
--- a/test/EFCore.Sqlite.FunctionalTests/Query/TPCGearsOfWarQuerySqliteTest.cs
+++ b/test/EFCore.Sqlite.FunctionalTests/Query/TPCGearsOfWarQuerySqliteTest.cs
@@ -301,6 +301,74 @@ public override async Task Byte_array_filter_by_SequenceEqual(bool async)
""");
}
+ #region Byte Array IndexOf Translation
+
+ public override async Task Byte_array_IndexOf_with_literal(bool async)
+ {
+ await base.Byte_array_IndexOf_with_literal(async);
+
+ AssertSql(
+ """
+SELECT "s"."Id", "s"."Banner", "s"."Banner5", "s"."InternalNumber", "s"."Name"
+FROM "Squads" AS "s"
+WHERE instr("s"."Banner", X'01') - 1 = 1
+""");
+ }
+
+ public override async Task Byte_array_IndexOf_with_parameter(bool async)
+ {
+ await base.Byte_array_IndexOf_with_parameter(async);
+
+ AssertSql(
+ """
+@__b_0='0'
+
+SELECT "s"."Id", "s"."Banner", "s"."Banner5", "s"."InternalNumber", "s"."Name"
+FROM "Squads" AS "s"
+WHERE instr("s"."Banner", char(@__b_0)) - 1 = 0
+""");
+ }
+
+ public override async Task Byte_array_with_length_IndexOf_with_literal(bool async)
+ {
+ await base.Byte_array_with_length_IndexOf_with_literal(async);
+
+ AssertSql(
+ """
+SELECT "s"."Id", "s"."Banner", "s"."Banner5", "s"."InternalNumber", "s"."Name"
+FROM "Squads" AS "s"
+WHERE instr("s"."Banner5", X'05') - 1 = 1
+""");
+ }
+
+ public override async Task Byte_array_with_length_IndexOf_with_parameter(bool async)
+ {
+ await base.Byte_array_with_length_IndexOf_with_parameter(async);
+
+ AssertSql(
+ """
+@__b_0='4'
+
+SELECT "s"."Id", "s"."Banner", "s"."Banner5", "s"."InternalNumber", "s"."Name"
+FROM "Squads" AS "s"
+WHERE instr("s"."Banner5", char(@__b_0)) - 1 = 0
+""");
+ }
+
+ public override Task Byte_array_IndexOf_with_startIndex_with_literals(bool async)
+ => AssertTranslationFailed(() => base.Byte_array_IndexOf_with_startIndex_with_literals(async));
+
+ public override Task Byte_array_IndexOf_with_startIndex_with_parameters(bool async)
+ => AssertTranslationFailed(() => base.Byte_array_IndexOf_with_startIndex_with_parameters(async));
+
+ public override Task Byte_array_with_length_IndexOf_with_startIndex_with_literals(bool async)
+ => AssertTranslationFailed(() => base.Byte_array_with_length_IndexOf_with_startIndex_with_literals(async));
+
+ public override Task Byte_array_with_length_IndexOf_with_startIndex_with_parameters(bool async)
+ => AssertTranslationFailed(() => base.Byte_array_with_length_IndexOf_with_startIndex_with_parameters(async));
+
+ #endregion
+
public override Task Where_TimeSpan_Hours(bool async)
// TimeSpan. Issue #18844.
=> AssertTranslationFailed(() => base.Where_TimeSpan_Hours(async));
diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/TPTGearsOfWarQuerySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/TPTGearsOfWarQuerySqliteTest.cs
index bef3059af39..474b99405f2 100644
--- a/test/EFCore.Sqlite.FunctionalTests/Query/TPTGearsOfWarQuerySqliteTest.cs
+++ b/test/EFCore.Sqlite.FunctionalTests/Query/TPTGearsOfWarQuerySqliteTest.cs
@@ -301,6 +301,74 @@ public override async Task Byte_array_filter_by_SequenceEqual(bool async)
""");
}
+ #region Byte Array IndexOf Translation
+
+ public override async Task Byte_array_IndexOf_with_literal(bool async)
+ {
+ await base.Byte_array_IndexOf_with_literal(async);
+
+ AssertSql(
+ """
+SELECT "s"."Id", "s"."Banner", "s"."Banner5", "s"."InternalNumber", "s"."Name"
+FROM "Squads" AS "s"
+WHERE instr("s"."Banner", X'01') - 1 = 1
+""");
+ }
+
+ public override async Task Byte_array_IndexOf_with_parameter(bool async)
+ {
+ await base.Byte_array_IndexOf_with_parameter(async);
+
+ AssertSql(
+ """
+@__b_0='0'
+
+SELECT "s"."Id", "s"."Banner", "s"."Banner5", "s"."InternalNumber", "s"."Name"
+FROM "Squads" AS "s"
+WHERE instr("s"."Banner", char(@__b_0)) - 1 = 0
+""");
+ }
+
+ public override async Task Byte_array_with_length_IndexOf_with_literal(bool async)
+ {
+ await base.Byte_array_with_length_IndexOf_with_literal(async);
+
+ AssertSql(
+ """
+SELECT "s"."Id", "s"."Banner", "s"."Banner5", "s"."InternalNumber", "s"."Name"
+FROM "Squads" AS "s"
+WHERE instr("s"."Banner5", X'05') - 1 = 1
+""");
+ }
+
+ public override async Task Byte_array_with_length_IndexOf_with_parameter(bool async)
+ {
+ await base.Byte_array_with_length_IndexOf_with_parameter(async);
+
+ AssertSql(
+ """
+@__b_0='4'
+
+SELECT "s"."Id", "s"."Banner", "s"."Banner5", "s"."InternalNumber", "s"."Name"
+FROM "Squads" AS "s"
+WHERE instr("s"."Banner5", char(@__b_0)) - 1 = 0
+""");
+ }
+
+ public override Task Byte_array_IndexOf_with_startIndex_with_literals(bool async)
+ => AssertTranslationFailed(() => base.Byte_array_IndexOf_with_startIndex_with_literals(async));
+
+ public override Task Byte_array_IndexOf_with_startIndex_with_parameters(bool async)
+ => AssertTranslationFailed(() => base.Byte_array_IndexOf_with_startIndex_with_parameters(async));
+
+ public override Task Byte_array_with_length_IndexOf_with_startIndex_with_literals(bool async)
+ => AssertTranslationFailed(() => base.Byte_array_with_length_IndexOf_with_startIndex_with_literals(async));
+
+ public override Task Byte_array_with_length_IndexOf_with_startIndex_with_parameters(bool async)
+ => AssertTranslationFailed(() => base.Byte_array_with_length_IndexOf_with_startIndex_with_parameters(async));
+
+ #endregion
+
public override Task Where_TimeSpan_Hours(bool async)
// TimeSpan. Issue #18844.
=> AssertTranslationFailed(() => base.Where_TimeSpan_Hours(async));