Skip to content

Commit 365cbc3

Browse files
authored
Fix NullReferenceException in SqlServerStringMethodTranslator.TranslateIndexOf for casted parameters (#37956)
Fixes #37940
1 parent ba4ebfe commit 365cbc3

4 files changed

Lines changed: 80 additions & 1 deletion

File tree

src/EFCore.SqlServer/Query/Internal/Translators/SqlServerStringMethodTranslator.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,8 @@ private SqlExpression TranslateIndexOf(
220220
SqlExpression searchExpression,
221221
SqlExpression? startIndex)
222222
{
223-
var stringTypeMapping = ExpressionExtensions.InferTypeMapping(instance, searchExpression)!;
223+
var stringTypeMapping = ExpressionExtensions.InferTypeMapping(instance, searchExpression)
224+
?? sqlExpressionFactory.ApplyDefaultTypeMapping(instance)!.TypeMapping!;
224225
searchExpression = sqlExpressionFactory.ApplyTypeMapping(
225226
searchExpression, searchExpression.Type == typeof(char) ? CharTypeMapping.Default : stringTypeMapping);
226227

test/EFCore.Relational.Specification.Tests/Query/Translations/StringTranslationsRelationalTestBase.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,4 +72,27 @@ public virtual Task Like_with_non_string_column_using_double_cast()
7272
ss => ss.Set<BasicTypesEntity>().Where(o => o.Int.ToString().Contains("5")));
7373

7474
#endregion Like
75+
76+
#region IndexOf
77+
78+
[ConditionalFact]
79+
public virtual Task IndexOf_with_non_string_column_using_double_cast()
80+
{
81+
var pattern = "5";
82+
return AssertQuery(
83+
ss => ss.Set<BasicTypesEntity>().Where(b => ((string)(object)b.Int).IndexOf(pattern) != -1),
84+
ss => ss.Set<BasicTypesEntity>().Where(b => b.Int.ToString().Contains(pattern)));
85+
}
86+
87+
#endregion IndexOf
88+
89+
#region Replace
90+
91+
[ConditionalFact]
92+
public virtual Task Replace_with_non_string_column_using_double_cast()
93+
=> AssertQuery(
94+
ss => ss.Set<BasicTypesEntity>().Where(b => ((string)(object)b.Int).Replace("8", "3") == "3"),
95+
ss => ss.Set<BasicTypesEntity>().Where(b => b.Int.ToString().Replace("8", "3") == "3"));
96+
97+
#endregion Replace
7598
}

test/EFCore.SqlServer.FunctionalTests/Query/Translations/StringTranslationsSqlServerTest.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,23 @@ ELSE 1
273273
""");
274274
}
275275

276+
public override async Task IndexOf_with_non_string_column_using_double_cast()
277+
{
278+
await base.IndexOf_with_non_string_column_using_double_cast();
279+
280+
AssertSql(
281+
"""
282+
@pattern='5' (Size = 4000)
283+
284+
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]
285+
FROM [BasicTypesEntities] AS [b]
286+
WHERE CAST(CHARINDEX(@pattern, CAST([b].[Int] AS nvarchar(max))) AS int) - CASE
287+
WHEN @pattern = N'' THEN 0
288+
ELSE 1
289+
END <> -1
290+
""");
291+
}
292+
276293
#endregion IndexOf
277294

278295
#region Replace
@@ -325,6 +342,18 @@ FROM [BasicTypesEntities] AS [b]
325342
""");
326343
}
327344

345+
public override async Task Replace_with_non_string_column_using_double_cast()
346+
{
347+
await base.Replace_with_non_string_column_using_double_cast();
348+
349+
AssertSql(
350+
"""
351+
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]
352+
FROM [BasicTypesEntities] AS [b]
353+
WHERE REPLACE(CAST([b].[Int] AS nvarchar(max)), N'8', N'3') = N'3'
354+
""");
355+
}
356+
328357
#endregion Replace
329358

330359
#region Substring

test/EFCore.Sqlite.FunctionalTests/Query/Translations/StringTranslationsSqliteTest.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,20 @@ WHERE instr('12559', CAST("b"."Int" AS TEXT)) - 1 = 1
263263
""");
264264
}
265265

266+
public override async Task IndexOf_with_non_string_column_using_double_cast()
267+
{
268+
await base.IndexOf_with_non_string_column_using_double_cast();
269+
270+
AssertSql(
271+
"""
272+
@pattern='5' (Size = 1)
273+
274+
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"
275+
FROM "BasicTypesEntities" AS "b"
276+
WHERE instr(CAST("b"."Int" AS TEXT), @pattern) - 1 <> -1
277+
""");
278+
}
279+
266280
#endregion IndexOf
267281

268282
#region Replace
@@ -315,6 +329,18 @@ public override async Task Replace_using_property_arguments()
315329
""");
316330
}
317331

332+
public override async Task Replace_with_non_string_column_using_double_cast()
333+
{
334+
await base.Replace_with_non_string_column_using_double_cast();
335+
336+
AssertSql(
337+
"""
338+
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"
339+
FROM "BasicTypesEntities" AS "b"
340+
WHERE replace(CAST("b"."Int" AS TEXT), '8', '3') = '3'
341+
""");
342+
}
343+
318344
#endregion Replace
319345

320346
#region Substring

0 commit comments

Comments
 (0)