-
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
_sqlAliasManager
unexpectedly null
in SelectExpression.PushdownIntoSubquery()
call after previous call of SelectExpression.Update()
#35192
Comments
Hey @lauxjpn 👋 Are you saying you call PushdownIntoSubquery at some point after in SqlNullabilityProcessor or afterwards, in your provider? That's not something we currently do elsewhere - can you provide a bit more context on where/why you need to do that? To give more context, SelectExpression (currently) has two modes: mutable and immutable; a SelectExpression is only mutable during the translation phase, as more operators get processed and the SelectExpression get mutated; right after translation it is made mutable. This is a design that I think is problematic in several ways, and I hope to be able to change in 10, so that our SQL tree is fully immutable. In any case, the SQL alias manager is currently only available in the mutable phase. Very concretely: rather than doing PushdownIntoSubquery(), which is meant to be used during the mutable phase, maybe it's possible to simply construct the new SelectExpression on top of the existing one (perform a "manual pushdown")? If not, I'll look into finding a solution for you. Unfortunately, this entire area is quite messy at the moment, and in a transitional phase... But I'm slowly trying to move us in a more sane direction. |
Yeah, we're back baby! Original postYes, we have a couple of expression visitors that we need to run as late as possible. I have now moved the one in question (the one that is doing the pushdown) into the post translation handling. The debugger shows though, that Since at this point, no call to Unfortunately, we can't be 100% sure that nobody has already called public static SelectExpression UpdateWithSqlAliasManager(
this SelectExpression selectExpression,
SqlAliasManager sqlAliasManager)
=> new(
selectExpression.Alias,
selectExpression.Tables.ToList(),
selectExpression.Predicate,
selectExpression.GroupBy.ToList(),
selectExpression.Having,
selectExpression.Projection.ToList(),
selectExpression.IsDistinct,
selectExpression.Orderings.ToList(),
selectExpression.Offset,
selectExpression.Limit,
selectExpression.Tags,
selectExpression.GetAnnotations().ToDictionary(a => a.Name, a => a),
sqlAliasManager,
false); We are still able to call this method in // In MySqlQueryTranslationPostprocessor.Process():
query = new MySqlHavingExpressionVisitor(
_sqlExpressionFactory,
((RelationalQueryCompilationContext)QueryCompilationContext).SqlAliasManager)
.Visit(query);
// In MySqlHavingExpressionVisitor.VisitSelect:
selectExpression = selectExpression.UpdateWithSqlAliasManager(_sqlAliasManager);
selectExpression.PushdownIntoSubquery(); The question is: Is it a bug that we are able to do this successfully in If we should not do this, we will either move the expression visitor to the end of the translation phase (but before (For context: The expression visitor in question has to do some weird syntax stuff for queries with I will reevaluate what we are doing with our HAVING expression visitor and come back to this, once I know in which direction we want to go with this. |
Thanks for all the information. Yeah, let me know where you end up. The current state of things really is transitional - I hope to be able to clean up a lot of this stuff in 10 and have a much better-factored, fully immutable SelectExpression and query pipeline... In the meantime, if you need a hack such as UpdateWithSqlAliasManager and don't mind having to react again for 10/11, then by all means go for it (I can see how something like that might be needed right now). And if you do get stuck, definitely don't hesitate to ping me again for assistance. I'll go ahead and close this in the meantime (nothing immediately actionable), but post back and I'll reopen as needed. Good luck! Looking forward to seeing the 9.0 release of the provider! |
I'll add my 2 cents as I had the same issue (@roji see #16038 (comment)) I did end up going a very similar route but with a couple of extras
The key part is I also needed to copy the private @lauxjpn Keep that in mind that you might run into that (I can't remember off hand what the scenarios were that triggered that problem). And good to see you're back |
@lauxjpn @ChrisJollyAU thanks for reporting on all this. I'll do my best to improve the overall query pipeline architecture so that hacks like these aren't needed. Thanks for being flexible/creative here in the meantime! |
@ChrisJollyAU Yeah, we hit the same problems along the way for Pomelo. Our goal is to make a pushdown of a query into a subquery and then change that subquery. We first tried to accomplish this in the part of the query pipeline, where the We then tried to do all the work after I deliberately left our non-working solutions in the codebase for now, so that @roji and the team can take a look at were we failed to continue with our approaches. You guys can take a look the MySqlNonWorkingHavingExpressionVisitor in our Upgrade to EF Core 9.0.0 PR, which showcases both efforts and contains a couple of comments. In the end, we split-up the work into a mutable tree part and an immutable tree part. In the mutable tree part, we just do the pushdown. In the immutable tree part, we then update the query. This is the cleanest approach that we came up with that does not use any hacks and is still maintainable. It is simple enough and contains the best of both worlds. The implementation is in MySqlHavingExpressionVisitor and we call it in var mySqlHavingExpressionVisitor = new MySqlHavingExpressionVisitor(_sqlExpressionFactory);
query = mySqlHavingExpressionVisitor.Process(query, usePrePostprocessorMode: true);
// Changes `SelectExpression.IsMutable` from `true` to `false`.
query = base.Process(query);
query = mySqlHavingExpressionVisitor.Process(query, usePrePostprocessorMode: false); |
Yeah, I continued on with that approach and used reflection for all the internal stuff. Now I just do
|
An existing
SelectExpression
is "updated" inSqlNullabilityProcessor.Visit(SelectExpression selectExpression, bool visitProjection)
via a call toSelectExpression.Update(...)
.On that resulting
SelectExpression
, we later callSelectExpression.PushdownIntoSubquery()
.Because
SelectExpression.PushdownIntoSubquery()
uses_sqlAliasManager
internally, andSelectExpression.Update(...)
does not retain theSqlAliasManager
instance of the originalSelectExpression
,_sqlAliasManager
isnull
in theSelectExpression.PushdownIntoSubquery()
call and an unexpected/implicit NRE is thrown.Include provider and version information
EF Core version: 9.0.0
Database provider: Pomelo.EntityFrameworkCore.MySql
The text was updated successfully, but these errors were encountered: