Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,21 @@ private static readonly MethodInfo ReplaceMethodInfo
private static readonly MethodInfo ContainsMethodInfo
= typeof(string).GetRuntimeMethod(nameof(string.Contains), [typeof(string)])!;

private static readonly MethodInfo ContainsWithStringComparisonMethodInfo
= typeof(string).GetRuntimeMethod(nameof(string.Contains), [typeof(string), typeof(StringComparison)])!;

private static readonly MethodInfo StartsWithMethodInfo
= typeof(string).GetRuntimeMethod(nameof(string.StartsWith), [typeof(string)])!;

private static readonly MethodInfo StartsWithWithStringComparisonMethodInfo
= typeof(string).GetRuntimeMethod(nameof(string.StartsWith), [typeof(string), typeof(StringComparison)])!;

private static readonly MethodInfo EndsWithMethodInfo
= typeof(string).GetRuntimeMethod(nameof(string.EndsWith), [typeof(string)])!;

private static readonly MethodInfo EndsWithWithStringComparisonMethodInfo
= typeof(string).GetRuntimeMethod(nameof(string.EndsWith), [typeof(string), typeof(StringComparison)])!;

private static readonly MethodInfo ToLowerMethodInfo
= typeof(string).GetRuntimeMethod(nameof(string.ToLower), [])!;

Expand Down Expand Up @@ -119,16 +128,79 @@ private static readonly MethodInfo StringComparisonWithComparisonTypeArgumentSta
return TranslateSystemFunction("CONTAINS", typeof(bool), instance, arguments[0]);
}

if (ContainsWithStringComparisonMethodInfo.Equals(method))
{
if (arguments[1] is SqlConstantExpression { Value: StringComparison comparisonType })
{
return comparisonType switch
{
StringComparison.Ordinal
=> TranslateSystemFunction(
"CONTAINS", typeof(bool), instance, arguments[0], sqlExpressionFactory.Constant(false)),
StringComparison.OrdinalIgnoreCase
=> TranslateSystemFunction(
"CONTAINS", typeof(bool), instance, arguments[0], sqlExpressionFactory.Constant(true)),

_ => null // TODO: Explicit translation error for unsupported StringComparison argument (depends on #26410)
};
}

// TODO: Explicit translation error for non-constant StringComparison argument (depends on #26410)
return null;
}

if (StartsWithMethodInfo.Equals(method))
{
return TranslateSystemFunction("STARTSWITH", typeof(bool), instance, arguments[0]);
}

if (StartsWithWithStringComparisonMethodInfo.Equals(method))
{
if (arguments[1] is SqlConstantExpression { Value: StringComparison comparisonType })
{
return comparisonType switch
{
StringComparison.Ordinal
=> TranslateSystemFunction(
"STARTSWITH", typeof(bool), instance, arguments[0], sqlExpressionFactory.Constant(false)),
StringComparison.OrdinalIgnoreCase
=> TranslateSystemFunction(
"STARTSWITH", typeof(bool), instance, arguments[0], sqlExpressionFactory.Constant(true)),

_ => null // TODO: Explicit translation error for unsupported StringComparison argument (depends on #26410)
};
}

// TODO: Explicit translation error for non-constant StringComparison argument (depends on #26410)
return null;
}

if (EndsWithMethodInfo.Equals(method))
{
return TranslateSystemFunction("ENDSWITH", typeof(bool), instance, arguments[0]);
}

if (EndsWithWithStringComparisonMethodInfo.Equals(method))
{
if (arguments[1] is SqlConstantExpression { Value: StringComparison comparisonType })
{
return comparisonType switch
{
StringComparison.Ordinal
=> TranslateSystemFunction(
"ENDSWITH", typeof(bool), instance, arguments[0], sqlExpressionFactory.Constant(false)),
StringComparison.OrdinalIgnoreCase
=> TranslateSystemFunction(
"ENDSWITH", typeof(bool), instance, arguments[0], sqlExpressionFactory.Constant(true)),

_ => null // TODO: Explicit translation error for unsupported StringComparison argument (depends on #26410)
};
}

// TODO: Explicit translation error for non-constant StringComparison argument (depends on #26410)
return null;
}

if (ToLowerMethodInfo.Equals(method))
{
return TranslateSystemFunction("LOWER", method.ReturnType, instance);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ public NorthwindFunctionsQueryCosmosTest(
public virtual void Check_all_tests_overridden()
=> TestHelpers.AssertAllMethodsOverridden(GetType());

#region String.StartsWith

public override Task String_StartsWith_Literal(bool async)
=> Fixture.NoSyncTest(
async, async a =>
Expand Down Expand Up @@ -96,6 +98,47 @@ FROM root c
""");
});

public override Task String_StartsWith_with_StringComparison_Ordinal(bool async)
=> Fixture.NoSyncTest(
async, async a =>
{
await base.String_StartsWith_with_StringComparison_Ordinal(a);

AssertSql(
"""
SELECT c
FROM root c
WHERE ((c["Discriminator"] = "Customer") AND STARTSWITH(c["CompanyName"], "Qu", false))
""");
});

public override Task String_StartsWith_with_StringComparison_OrdinalIgnoreCase(bool async)
=> Fixture.NoSyncTest(
async, async a =>
{
await base.String_StartsWith_with_StringComparison_OrdinalIgnoreCase(a);

AssertSql(
"""
SELECT c
FROM root c
WHERE ((c["Discriminator"] = "Customer") AND STARTSWITH(c["CompanyName"], "Qu", true))
""");
});

public override async Task String_StartsWith_with_StringComparison_unsupported(bool async)
{
// Always throws for sync.
if (async)
{
await base.String_StartsWith_with_StringComparison_unsupported(async);
}
}

#endregion String.StartsWith

#region String.EndsWith

public override Task String_EndsWith_Literal(bool async)
=> Fixture.NoSyncTest(
async, async a =>
Expand Down Expand Up @@ -168,6 +211,47 @@ FROM root c
""");
});

public override Task String_EndsWith_with_StringComparison_Ordinal(bool async)
=> Fixture.NoSyncTest(
async, async a =>
{
await base.String_EndsWith_with_StringComparison_Ordinal(a);

AssertSql(
"""
SELECT c
FROM root c
WHERE ((c["Discriminator"] = "Customer") AND ENDSWITH(c["ContactName"], "DY", false))
""");
});

public override Task String_EndsWith_with_StringComparison_OrdinalIgnoreCase(bool async)
=> Fixture.NoSyncTest(
async, async a =>
{
await base.String_EndsWith_with_StringComparison_OrdinalIgnoreCase(a);

AssertSql(
"""
SELECT c
FROM root c
WHERE ((c["Discriminator"] = "Customer") AND ENDSWITH(c["ContactName"], "DY", true))
""");
});

public override async Task String_EndsWith_with_StringComparison_unsupported(bool async)
{
// Always throws for sync.
if (async)
{
await base.String_EndsWith_with_StringComparison_unsupported(async);
}
}

#endregion String.EndsWith

#region String.Contains

public override Task String_Contains_Literal(bool async)
=> Fixture.NoSyncTest(
async, async a =>
Expand Down Expand Up @@ -209,6 +293,45 @@ FROM root c
""");
});

public override Task String_Contains_with_StringComparison_Ordinal(bool async)
=> Fixture.NoSyncTest(
async, async a =>
{
await base.String_Contains_with_StringComparison_Ordinal(a);

AssertSql(
"""
SELECT c
FROM root c
WHERE ((c["Discriminator"] = "Customer") AND CONTAINS(c["ContactName"], "M", false))
""");
});

public override Task String_Contains_with_StringComparison_OrdinalIgnoreCase(bool async)
=> Fixture.NoSyncTest(
async, async a =>
{
await base.String_Contains_with_StringComparison_OrdinalIgnoreCase(a);

AssertSql(
"""
SELECT c
FROM root c
WHERE ((c["Discriminator"] = "Customer") AND CONTAINS(c["ContactName"], "M", true))
""");
});

public override async Task String_Contains_with_StringComparison_unsupported(bool async)
{
// Always throws for sync.
if (async)
{
await base.String_Contains_with_StringComparison_unsupported(async);
}
}

#endregion String.Contains

public override Task String_FirstOrDefault_MethodCall(bool async)
=> Fixture.NoSyncTest(
async, async a =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,16 @@
namespace Microsoft.EntityFrameworkCore.Query;

public class NorthwindFunctionsQueryInMemoryTest(NorthwindQueryInMemoryFixture<NoopModelCustomizer> fixture)
: NorthwindFunctionsQueryTestBase<NorthwindQueryInMemoryFixture<NoopModelCustomizer>>(fixture);
: NorthwindFunctionsQueryTestBase<NorthwindQueryInMemoryFixture<NoopModelCustomizer>>(fixture)
{
// StringComparison.CurrentCulture{,IgnoreCase} and InvariantCulture{,IgnoreCase} are not supported in real providers, but the in-memory
// provider does support them.
public override Task String_StartsWith_with_StringComparison_unsupported(bool async)
=> Task.CompletedTask;

public override Task String_EndsWith_with_StringComparison_unsupported(bool async)
=> Task.CompletedTask;

public override Task String_Contains_with_StringComparison_unsupported(bool async)
=> Task.CompletedTask;
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,36 @@ protected NorthwindFunctionsQueryRelationalTestBase(TFixture fixture)
{
}

// StartsWith with StringComparison not supported in relational databases, where the column collation is used to control comparison
// semantics.
public override Task String_StartsWith_with_StringComparison_Ordinal(bool async)
=> AssertTranslationFailed(() => base.String_StartsWith_with_StringComparison_Ordinal(async));

// StartsWith with StringComparison not supported in relational databases, where the column collation is used to control comparison
// semantics.
public override Task String_StartsWith_with_StringComparison_OrdinalIgnoreCase(bool async)
=> AssertTranslationFailed(() => base.String_StartsWith_with_StringComparison_OrdinalIgnoreCase(async));

// EndsWith with StringComparison not supported in relational databases, where the column collation is used to control comparison
// semantics.
public override Task String_EndsWith_with_StringComparison_Ordinal(bool async)
=> AssertTranslationFailed(() => base.String_EndsWith_with_StringComparison_Ordinal(async));

// EndsWith with StringComparison not supported in relational databases, where the column collation is used to control comparison
// semantics.
public override Task String_EndsWith_with_StringComparison_OrdinalIgnoreCase(bool async)
=> AssertTranslationFailed(() => base.String_EndsWith_with_StringComparison_OrdinalIgnoreCase(async));

// Contains with StringComparison not supported in relational databases, where the column collation is used to control comparison
// semantics.
public override Task String_Contains_with_StringComparison_Ordinal(bool async)
=> AssertTranslationFailed(() => base.String_Contains_with_StringComparison_Ordinal(async));

// Contains with StringComparison not supported in relational databases, where the column collation is used to control comparison
// semantics.
public override Task String_Contains_with_StringComparison_OrdinalIgnoreCase(bool async)
=> AssertTranslationFailed(() => base.String_Contains_with_StringComparison_OrdinalIgnoreCase(async));

protected override QueryAsserter CreateQueryAsserter(TFixture fixture)
=> new RelationalQueryAsserter(
fixture, RewriteExpectedQueryExpression, RewriteServerQueryExpression);
Expand Down
Loading