-
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
Query: Assign aliases to tables differently #17337
Comments
This also has an important perf aspect, since we're visiting the same TableExpressions via both projections and tables. #17455 shows an extreme, exponential effect where only 7 includes cause over 20 seconds of query compilation time. Clearing milestone to consider for 3.1. |
"Clearing milestone to consider for 3.1." - unless you plan to pass 3.1 within 4 weeks of 3.0 or so that means for me and a lot of people like me EFCore is totally absolutely UNUSABLE because we DO have 10+ includes in large parts of our application and if you go from "instant compilation" to "minutes" that is a regression that is NOT ready even for development. |
Any objection to me poaching this @smitpatel? |
Yes. I will fix this. |
@roji - If you have ideas around it and want to work on this, then please schedule a meeting and bring a design. I want to avoid you doing work and then having to discard because of lack of communication around complexity and areas this would touch. |
At least as a first step, the idea was simply to collect old to new table mappings as we visit SelectExpression.Tables, and then use ReplacingExpressionVisitor on each query component before visiting it. You've already removed visitation in ColumnExpression in #17651, so unless we have other forms of references to tables from within query constructs, it seems like it should take care of it. Am I missing other aspects? |
How would you do it if Visitation of SelectExpression is outside of your control? i.e. what if SelectExpression.VisitChildren is not used? |
At least for 3.1, where we're still doing low-risk bugfixes only, it may be enough to identify current visitors which visit SelectExpression themselves and apply the same logic (e.g. SearchConditionConvertingExpressionVisitor, not sure if there are others). This is similar to what you've done in #17651 - we no longer visit tables via ColumnExpressions but badly-written visitors may still do that. A more extreme approach would be to factor out the various visitations from VisitChildren (e.g. VisitProjections, VisitPredicate), and not allow SqlExpressionVisitor subclasses to override VisitSelect, locking down the correct behavior. Since we have very little visitors at the moment, it's difficult to know whether that would prevent useful/legitimate overriding scenarios, so it doesn't seem right (or at least premature). Even if we do this, it's always possible for a visitor to extend ExpressionVisitor directly and mess up a SelectExpression in various ways. Finally, we can add a verification (debug-only?) visitor at end of postprocessing to verify that referential integrity is maintained, to catch any bugs (like SqlTypeMappingVerifyingExpressionVisitor for missing type mappings). But if I'm missing something or if you have something different in mind for 3.1 we can just have a conversation about it. |
None of those fixes the crux of the issue. While they resolve things for internal visitors, they still leave scope for external ones to do things incorrectly. If that is what we are heading at (fixing for internal visitors), then the solution we have in codebase right now is good enough. There are altogether different ideas how to deal with this so that this problem does not arise in first place. Hence my hesitance over this issue being poachable. |
Unless I'm mistaken, #17651 doesn't fix the referential integrity issue in any way - only the perf issue (by skipping visitation in ColumnExpression) - so in the current codebase, ColumnExpressions currently point to potentially old tables which may have been replaced by visitors. It seems to me that this is well worth fixing in 3.1 regardless of external visitors, but I may be mistaken (i.e. there may not be an actual problem caused by this at the moment). An airtight solution with regards to external visitors would lock down access to SelectExpression.Tables, so that if a visitor wants to replace a table they'd have through a special method on SelectExpression (e.g. ReplaceTable), which would also perform the replacement in all query components. Depending on how far we want to go, we may also want to prevent the possibility of a visitor to replace a ColumnExpression with another that refers to another table (TableExpression and SelectExpression have internal-only constructors, but not set operations). I assumed such design changes would be a too much for 3.1, but again maybe you see things differently. I'm fine discussing what to do, or if you prefer I can leave this issue to you. |
…or referential integrity Part of #17337 - Add TableReferenceExpression - SelectExpression contains tableReferenceExpression for each table added - TableReferenceExpression can identify the table by querying the selectExpression - During pushdown and set operation we update the table references as the selectExpression where columns belong is changing - Whenever adding something to selectExpression, we assign unique alises if there is nested selectExpression - Improve identifier detection for Distinct/GroupBy cases - Pushdown internally gives a visitor to update the expression being added in terms of new column expressions - Copy proper identifiers when applying set operation - Make dictionary being passed around in query IReadOnlyDictionary - Move entityProjectionCache from QueryExpression to ProjectionBindingExpressionVisitor as it is not an internal state for QueryExpression. It existing only in the context of applying particular projection mapping once. - Remove TableAliasUniquifyingExpressionVisitor, added debug only (currently disabled) TableAliasVerifyingExpressionVisitor which verifies that all alises are unique and used in order.
…or referential integrity Part of #17337 - Add TableReferenceExpression - SelectExpression contains tableReferenceExpression for each table added - TableReferenceExpression can identify the table by querying the selectExpression - During pushdown and set operation we update the table references as the selectExpression where columns belong is changing - Whenever adding something to selectExpression, we assign unique alises if there is nested selectExpression - Improve identifier detection for Distinct/GroupBy cases - Pushdown internally gives a visitor to update the expression being added in terms of new column expressions - Copy proper identifiers when applying set operation - Make dictionary being passed around in query IReadOnlyDictionary - Move entityProjectionCache from QueryExpression to ProjectionBindingExpressionVisitor as it is not an internal state for QueryExpression. It existing only in the context of applying particular projection mapping once. - Remove TableAliasUniquifyingExpressionVisitor, added debug only (currently disabled) TableAliasVerifyingExpressionVisitor which verifies that all alises are unique and used in order.
Once query start doing client eval, if there are operators afterwards which are binding to index in Projection list, now we translate it if we get the ProjectionBindingExpression. We cannot translate the part which is generated by client Some cleanup for #17337
Part of #17337 This commit doesn't make any functional change. Just moved code around for better organization and made certain private functions local static functions
…or referential integrity - Add TableReferenceExpression - SelectExpression contains tableReferenceExpression for each table added - TableReferenceExpression can identify the table by querying the selectExpression - During pushdown and set operation we update the table references as the selectExpression where columns belong is changing - Whenever adding something to selectExpression, we assign unique alises if there is nested selectExpression - Improve identifier detection for Distinct/GroupBy cases - Pushdown internally gives a visitor to update the expression being added in terms of new column expressions - Copy proper identifiers when applying set operation - Make dictionary being passed around in query IReadOnlyDictionary - Move entityProjectionCache from QueryExpression to ProjectionBindingExpressionVisitor as it is not an internal state for QueryExpression. It existing only in the context of applying particular projection mapping once. - Remove TableAliasUniquifyingExpressionVisitor, added debug only (currently disabled) TableAliasVerifyingExpressionVisitor which verifies that all alises are unique and used in order. Resolves #17337
Part of #17337 This commit doesn't make any functional change. Just moved code around for better organization and made certain private functions local static functions
…or referential integrity - Add TableReferenceExpression - SelectExpression contains tableReferenceExpression for each table added - TableReferenceExpression can identify the table by querying the selectExpression - During pushdown and set operation we update the table references as the selectExpression where columns belong is changing - Whenever adding something to selectExpression, we assign unique alises if there is nested selectExpression - Improve identifier detection for Distinct/GroupBy cases - Pushdown internally gives a visitor to update the expression being added in terms of new column expressions - Copy proper identifiers when applying set operation - Make dictionary being passed around in query IReadOnlyDictionary - Move entityProjectionCache from QueryExpression to ProjectionBindingExpressionVisitor as it is not an internal state for QueryExpression. It existing only in the context of applying particular projection mapping once. - Remove TableAliasUniquifyingExpressionVisitor, added debug only (currently disabled) TableAliasVerifyingExpressionVisitor which verifies that all alises are unique and used in order. Resolves #17337
- Don't apply Include on entities with Include already applied - Update table references when pushing down select into left for set operation - Update identifiers after applying set operation if the projection removed exiting identifiers - Update SQL references in pending collection during push down Fix for the repro in #17337 Resolves #18738 Resolves #19763 Resolves #19947 Resolves #20813 Resolves #21026 Resolves #22222 Resolves #23676 Resolves #23720
- Don't apply Include on entities with Include already applied - Update table references when pushing down select into left for set operation - Update identifiers after applying set operation if the projection removed exiting identifiers - Update SQL references in pending collection during push down Fix for the repro in #17337 Resolves #18738 Resolves #19763 Resolves #19947 Resolves #20813 Resolves #21026 Resolves #22222 Resolves #23676 Resolves #23720 Resolves #24216
- Don't apply Include on entities with Include already applied - Update table references when pushing down select into left for set operation - Update identifiers after applying set operation if the projection removed exiting identifiers - Update SQL references in pending collection during push down Fix for the repro in #17337 Resolves #18738 Resolves #19763 Resolves #19947 Resolves #20813 Resolves #21026 Resolves #22222 Resolves #23676 Resolves #23720 Resolves #24216
- Don't apply Include on entities with Include already applied - Update table references when pushing down select into left for set operation - Update identifiers after applying set operation if the projection removed exiting identifiers - Update SQL references in pending collection during push down Fix for the repro in #17337 Resolves #18738 Resolves #19763 Resolves #19947 Resolves #20813 Resolves #21026 Resolves #22222 Resolves #23676 Resolves #23720 Resolves #24216
- Don't apply Include on entities with Include already applied - Update table references when pushing down select into left for set operation - Update identifiers after applying set operation if the projection removed exiting identifiers - Update SQL references in pending collection during push down Fix for the repro in #17337 Resolves #18738 Resolves #19763 Resolves #19947 Resolves #20813 Resolves #21026 Resolves #22222 Resolves #23676 Resolves #23720 Resolves #24216
A selectExpression contains projections & other constructs, which refers to tables in that select expression. When visiting children of select expression if that table is updated then it causes re-generation for each node using it. Hence the constraint is violated. While it has low functional impact but it means that we need to do deep equals every time we want to compare. Ideally, VisitChildren should take care of re-connecting nodes.
The text was updated successfully, but these errors were encountered: