Skip to content

Fix OData string comparisons (lt, gt, ge, le) #2386

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

Merged
merged 10 commits into from
Jun 2, 2020

Conversation

bahusoid
Copy link
Member

@bahusoid bahusoid commented May 17, 2020

Fixes #2362

Support StringComparison parameter with warning for string methods (StartsWith, EndsWith, IndexOf, Replace)

@bahusoid bahusoid changed the title Fix ODATA string comparisons (lt, gt, ge, le etc..) Fix OData string comparisons (lt, gt, ge, le) May 17, 2020
[TestCase("$filter=CustomerId le 'ANATR'",2 )]
[TestCase("$filter=startswith(CustomerId, 'ANATR')", 1)]
[TestCase("$filter=endswith(CustomerId, 'ANATR')", 1)]
[TestCase("$filter=indexof(CustomerId, 'ANATR') eq 0", 1)]
Copy link
Member

Choose a reason for hiding this comment

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

Since we do not register EndsWith, StartsWith or IndexOf overloads with StringComparison, does this means there is an inconsistency in OData implementation here? It would emit Compare calls with that argument, but it would omit it for these three other cases?

So I see three options:

  • only register what is currently required by OData, as does this PR currently.
  • also register other cases overloads in case OData changes on this, or in case another Linq generator library requires them.
  • reject this as an external issue, to be fixed on OData side.

@maca88, @hazzik

Copy link
Member Author

Choose a reason for hiding this comment

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

does this means there is an inconsistency in OData implementation here?

Yeap. Seems to be.

also register other cases overloads in case OData changes on this, or in case another Linq generator library requires them.

I would choose this one as I see no difference with call to default string.Compare. It's not corelated in any way to with how DB is configured to make string comparisons. In fact StringComparison might be used to make LINQ to objects queries behave the same as DB (as I already mentioned in case of default SQL Server case insensitive collation)

Copy link
Member

Choose a reason for hiding this comment

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

Ok.

Copy link
Contributor

Choose a reason for hiding this comment

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

By just adding the missing overload we introduce another issue where we generate the same query when using StringComparison.XXX and StringComparison.XXXIgnoreCase, which would cause an unexpected result on a case sensitive collation. IMO we should at least try to mimic .NET behavior when using XXXIgnoreCase by using lower function on each side.

does this means there is an inconsistency in OData implementation here?

Yes, from the OData source code only StringComparison.Ordinal option is used for string.Compare, where for EndsWith, StartsWith and other methods the StringComparison overload is not used.

Copy link
Member

@fredericDelaporte fredericDelaporte May 17, 2020

Choose a reason for hiding this comment

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

By just adding the missing overload we introduce another issue where we generate the same query when using StringComparison.XXX and StringComparison.XXXIgnoreCase, which would cause an unexpected result on a case sensitive collation.

The thing is, we do not try to support different StringComparison values, we just ignore them. The trouble is the same conversely, with case insensitive collations, when using a non ignoring case StringComparison. And for this converse case, there is no ".Net" side workaround.

The current state is, NHibernate bluntly states these overloads are not supported. That is fine for user generated expression. But when the user cannot control the generated expression, it is a blocker.

So I think accepting these overloads, while not actually supporting the StringComparison argument, is an acceptable trade-of. Maybe we should emit a NHibernate warning or information about the argument being ignored, though.

Copy link
Contributor

@maca88 maca88 May 19, 2020

Choose a reason for hiding this comment

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

And for this converse case, there is no ".Net" side workaround

Some databases support the COLLATE clause (SqlLite, SqlServer, Oracle, PostgreSql, MySql) within the query, which could be used to do case sensitive/insensitive comparison. But that's a matter for a separate PR.

So I think accepting these overloads, while not actually supporting the StringComparison argument, is an acceptable trade-of

It is fine by me.

@hazzik
Copy link
Member

hazzik commented May 19, 2020

Maybe we should check that the argument is StringComparison.Ordinal and if it is not - throw an exception?

@maca88
Copy link
Contributor

maca88 commented May 19, 2020

if it is not - throw an exception?

I would lean more towards throwing an exception than just ignoring the argument, but I fine with both approaches.

@bahusoid
Copy link
Member Author

I don't understand why Ordinal should be supported but not CurrentCulture or InvariantCulture. Specific fix for ODATA behavior? Again for SQL server equivalent would be something like OrdinalIgnoreCase. Supporting only Ordinal makes it look more like a strange hack to me... So I agree with Frederic about warn and simply ignoring this param.

bahusoid added 2 commits May 21, 2020 11:56
'instr' supported since SQLite 3.7.15. So maybe better be placed in separate class or in separate PR
@fredericDelaporte fredericDelaporte added this to the 5.3 milestone May 31, 2020
@fredericDelaporte fredericDelaporte merged commit 8e98169 into nhibernate:master Jun 2, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add support for lt, gt, le, ge oData operators on strings
4 participants