Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid using ^ and ~ when invalid because of value converters #35124

Merged
merged 2 commits into from
Nov 29, 2024

Conversation

ranma42
Copy link
Contributor

@ranma42 ranma42 commented Nov 16, 2024

The transformation of equality/in-equality in a (negated) XOR is only possible
when the expressions are BIT or integer types on the SQL side (i.e. taking value
conversion into account).

Similarly, the Boolean negation NOT can be implemented as ~ only if the
underlying expression is a BIT.

Fixes #35093.

@ranma42 ranma42 marked this pull request as ready for review November 16, 2024 16:13
@ranma42 ranma42 requested a review from a team as a code owner November 16, 2024 16:13
Copy link
Member

@roji roji left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ranma42 thanks for working on this and sorry it took a while to review (lots of workload at the moment post-9.0!).

See my two comments and let me know what you think... FYI the deadline for getting a fix into 9.0.1 is extremely close (ideally we'd have this finished today). If you can, please let me know if you don't have time to iterate over this right away - I'm happy to take over and do changes.

Once we're good here I'll also prepare a backport PR for 9.0.1.

if (!inSearchConditionContext
&& (newLeft.Type == typeof(bool) || newLeft.Type.IsEnum || newLeft.Type.IsInteger())
&& (newRight.Type == typeof(bool) || newRight.Type.IsEnum || newRight.Type.IsInteger()))
&& (leftType == typeof(bool) || leftType.IsEnum || leftType.IsInteger())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the IsEnum case can be removed, since now that we're looking at the provider type is should never be an enum (enums are value-converted to int)

@@ -265,7 +267,9 @@ protected virtual Expression VisitSqlUnary(SqlUnaryExpression sqlUnaryExpression
case ExpressionType.Not when sqlUnaryExpression.Type == typeof(bool):
{
// when possible, avoid converting to/from predicate form
if (!inSearchConditionContext && sqlUnaryExpression.Operand is not (ExistsExpression or InExpression or LikeExpression))
if (!inSearchConditionContext &&
sqlUnaryExpression.TypeMapping?.Converter is null &&
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't a more correct fix here to look at sqlUnaryExpression.TypeMapping?.Converter?.ProviderClrType ?? sqlUnaryExpression.Type above (in the when comparing to typeof(bool))? For example, if there's a Y/N string being value converted to BIT, I think we do want this code to go through and do OnesComponent (on the database side it's just a BIT).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yup, I just thought that it was not a very likely scenario; will update ASAP

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You'd be surprised what people do 🤣

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not even sure if it is possible to build an expression in which a y/n string is being negated 🤔
I applied your suggestion, but I don't have time to add a test for this case right now (I can work on it in the weekend, if needed).

Copy link
Member

@roji roji Nov 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're generally completely right... Though I'll point out that Expression.Not() has an overload which accepts an implementing method - this allows arbitrary types to be "negatable". Similarly, IIRC user types which implement the not operator via operator overloading should also be supported (by am no longer sure about that).

Of course, it could be argued that EF shouldn't translate negation for those cases (where the implementing method/operator obviously are not taken into account)... But this is all indeed highly academic. I also don't think a test is necesary here at this point.

@ranma42
Copy link
Contributor Author

ranma42 commented Nov 28, 2024

I will try to push the changes tonight (aka in ~4 hours)

The transformation of equality/in-equality in a (negated) XOR is only possible
when the expressions are BIT or integer types on the SQL side (i.e. taking value
conversion into account).

Similarly, the Boolean negation `NOT` can be implemented as `~` only if the
underlying expression is a BIT.

Fixes dotnet#35093.
Copy link
Member

@roji roji left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks as always @ranma42!

@roji roji merged commit e6abfdd into dotnet:main Nov 29, 2024
7 checks passed
roji pushed a commit to roji/efcore that referenced this pull request Nov 29, 2024
…net#35124)

The transformation of equality/in-equality in a (negated) XOR is only possible
when the expressions are BIT or integer types on the SQL side (i.e. taking value
conversion into account).

Similarly, the Boolean negation `NOT` can be implemented as `~` only if the
underlying expression is a BIT.

Fixes dotnet#35093.

(cherry picked from commit e6abfdd)
@roji
Copy link
Member

roji commented Nov 30, 2024

Backport PR: #35241

roji added a commit that referenced this pull request Dec 2, 2024
) (#35241)

The transformation of equality/in-equality in a (negated) XOR is only possible
when the expressions are BIT or integer types on the SQL side (i.e. taking value
conversion into account).

Similarly, the Boolean negation `NOT` can be implemented as `~` only if the
underlying expression is a BIT.

Fixes #35093.

(cherry picked from commit e6abfdd)

Co-authored-by: Andrea Canciani <[email protected]>
@ranma42 ranma42 deleted the fix-35093 branch December 15, 2024 12:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

The data types varchar and nvarchar are incompatible in the '^' operator
2 participants