-
Notifications
You must be signed in to change notification settings - Fork 3.2k
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
Use right hand type mapping for more binary expressions when left is an object #34729
Conversation
… some tests that were similar
@ChrisJollyAU sorry for delayed response. Changes look good. Wrt testing, we try to put straightforward queries into classes that use AssertQuery. This way we take advantage of result verification and some other perks. AdHoc should generally be used for tests with complicated mappings (not covered by AssertQuery models) or if there is some other gimmick that is required for the test. There are a lot of those AssertQuery classes and it can sometimes be hard to pick the best place. When in doubt, you can always use GearsOfWar class, which is our dumping ground for all sort of query tests. When it comes to sql verification, we do require SQL Server, Sqlite is usually very similar to there is no point. If there is some specific sqlite construct or translation differs significantly between sql server and sqlite, it's good idea to add the SQL verification. But we don't require it. Cosmos doesn't translate this scenario - added translation error verification, so that if in the future the scenario is enabled we will catch it. Also, I removed some of the tests, there is no need to do all the combinations. It took me way to long to reply, so I made the testing changes, rather than burden you with them. |
Thank you for the contribution @ChrisJollyAU ! |
@maumar Thanks for sorting the tests out. You are right that we probably didn't need all those combinations - especially when things like contains/startswith/endswith all follow the same path (at least in sql server) Was happy to wait for you to get to it - I expect the current priority was finalizing EFCore 9. Quick question: Do I need to a backport PR or is this just going to stay on the 10.0 branch? |
@maumar maybe Cosmos doesn't translate this particular query, but isn't the fix as-is relevant to Cosmos as well (whose SqlExpressionFactory was basically a copy-paste of the relational one)? We really should start keeping Cosmos in mind and applying fixes there too, to not have to deal with the same bugs again there later... |
@roji It's actually a bit of a deeper problem on Cosmos than just this fix I installed Cosmos Emulator so I could take a look. It's actually failing on the string concatenation part in the binary expression. So its this expression: First of all, at efcore/src/EFCore.Cosmos/Query/Internal/CosmosSqlTranslatingExpressionVisitor.cs Line 189 in 931a67c
it automatically returns the not translated when we have a If we remove that and let it continue, we get much further but not quite. Also in The resultant binary expression then ends up with |
@ChrisJollyAU thanks for taking a look... Yeah, the Cosmos provider is definitely behind in many ways - it was basically copy-pasted from relational a long time ago, and not well-maintained since (we did a big push for 9.0 but there's a lot of stuff will remaining). I do have some ideas on how to try to merge more parts of Cosmos and relational so we have less duplication to manage, but in the meantime this situation is precisely why we should try to apply fixes to both relational and Cosmos, to keep them from diverging even further. Concretely, it sounds like a new issue is needed for the string concatenation problem you ran into. However, the specific fix in this PR seems correct regardless of whether it actually fixes any specific bugs/tests (hopefully it doesn't break any...). So I'd still port the fix across, unless you think there's some reason not to? |
Sure, we can apply this fix. It's just a 1 liner. Quick run through locally doesn't show any that it breaks. (I do have some in JsonQueryCosmosTest that fail on a JsonSerializationException but that doesn't look like anything to do with this error and the tests have the First time I'm looking at Cosmos and did find myself wondering why things like the |
Definitely new issue. Note that test like |
Great, thanks! Feel free to submit a PR.
There are non-trivial reasons for this... For example, in the query pipeline design, SqlExpression represents a scalar and so references a type mapping (which describes the type of that scalar), but type mappings are provider-specific (RelationalTypeMapping vs. CosmosTypeMapping). This causes the need to duplicate SqlExpression, and from there also SqlExpressionFactory. I definitely agree that we should rethink this design and see how we can get mure reuse across the providers - but that's a deep architectural task that we don't currently have the bandwidth for (there are more urgent deep architectural tasks that would get priority)... So unfortunately for now we need to do our best to keep things more or less aligned, e.g. fixing bugs on both. |
Yeah. that might be a whole feature release on its own |
With a query like
Select(x => x != null ? x.Id + "" : null).FirstOrDefault(x => x!.Contains("1"));
you have a binary expression ofx.Id + ""
. The left hand side comes through from LINQ without any type mapping and when inferring the type mapping for the result of the binary expression it always looks at the left hand side so you end up with a Type ofobject
and a null type mapping for the expressionWith the order of the
Select
it ends up trying to take the type mapping ofx.Id + ""
for the result of theCASE
. When you then do aStartsWith
,EndsWith
.Contains
all those are translated as aLIKE
and no type mapping is changed.It works with the equals
Select(x => x != null ? x.Id + "" : null).FirstOrDefault(x => x == "1");
as that code does look at the right hand side if the left is an object.See line 185 in SqlExpressionFactory
When you flip the Select around (
Select(x => x == null ? null : x.Id + "").FirstOrDefault(x => x!.Contains("1"));
) it works because thenull
result with type mapping of string is used for the result of theCASE
and the type mapping is applied on all expressionsThis change adds the same type of logic elsewhere in the
ApplyTypeMappingOnSqlBinary
for the Add, Subtract etc operationsTests added in AdHocMiscellaneousQueryTestBase.
Fixes #34618