src/Shared/EntityFramework/Core/Common/EntitySql/SemanticResolver.cs
// <summary>
// Returns the appropriate expression from a given scope entry.
// May return null for scope entries like <see cref="InvalidGroupInputRefScopeEntry" />.
// </summary>
Returns the appropriate expression from a given scope entry.
May return null for scope entries like .
Returns the appropriate expression from a given scope entry . May return null for scope entries like .
private DbExpression GetExpressionFromScopeEntry(ScopeEntry scopeEntry, int scopeIndex, string varName, ErrorContext errCtx)
        {
             If
                  1) we are in the context of a group aggregate or group key, 
                  2) and the scopeEntry can have multiple interpretations depending on the aggregation context,
                  3) and the defining scope region of the scopeEntry is outer or equal to the defining scope region of the group aggregate,
                  4) and the defining scope region of the scopeEntry is not performing conversion of a group key definition,
             Then the expression that corresponds to the scopeEntry is either the GroupVarBasedExpression or the GroupAggBasedExpression.
             Otherwise the default expression that corresponds to the scopeEntry is provided by scopeEntry.GetExpression(...) call.
             Explanation for #2 from the list above:
             A scope entry may have multiple aggregation-context interpretations:
                  - An expression in the context of a group key definition, obtained by scopeEntry.GetExpression(...);
                    Example: select k1 from {0} as a group by a%2 as k1
                                                              ^^^
                  - An expression in the context of a function aggregate, provided by iGroupExpressionExtendedInfo.GroupVarBasedExpression;
                    Example: select max( a ) from {0} as a group by a%2 as k1
                                        ^^^
                  - An expression in the context of a group partition, provided by iGroupExpressionExtendedInfo.GroupAggBasedExpression;
                    Example: select GroupPartition( a ) from {0} as a group by a%2 as k1
                                                   ^^^
             Note that expressions obtained from aggregation-context-dependent scope entries outside of the three contexts mentioned above
             will default to the value returned by the scopeEntry.GetExpression(...) call. This value is the same as in the group key definition context.
             These expressions have correct result types which enables partial expression validation. 
             However the contents of the expressions are invalid outside of the group key definitions, hence they can not appear in the final expression tree.
             SemanticAnalyzer.ProcessGroupByClause(...) method guarantees that such expressions are only temporarily used during GROUP BY clause processing and
             dropped afterwards.
             Example: select a, k1 from {0} as a group by a%2 as k1
                             ^^^^^ - these expressions are processed twice: once during GROUP BY and then SELECT clause processing,
                                     the expressions obtained during GROUP BY clause processing are dropped and only
                                     the ones obtained during SELECT clause processing are accepted.
             Explanation for #3 from the list above:
                  - An outer scope entry referenced inside of an aggregate may lift the aggregate to the outer scope region for evaluation, 
                    hence such a scope entry must be interpreted in the aggregation context. See explanation for #4 below for more info.
                    Example: 
                      select 
                          (select max(x) from {1} as y) 
                      from {0} as x
                  - If a scope entry is defined inside of a group aggregate, then the scope entry is not affected by the aggregate, 
                    hence such a scope entry is not interpreted in the aggregation context.
                    Example:
                      select max(
                                   anyelement( select b from {1} as b )  
                                )
                      from {0} as a group by a %2 as a1
                    In this query the aggregate argument contains a nested query expression.
                    The nested query references b. Because b is defined inside of the aggregate it is not interpreted in the aggregation context and
                    the expression for b should not be GroupVar/GroupAgg based, even though the reference to b appears inside of an aggregate.
             Explanation for #4 from the list above:
             An aggregate evaluating on a particular scope region defines the interpretation of scope entries defined on that scope region.
             In the case when an inner aggregate references a scope entry belonging to the evaluating region of an outer aggregate, the interpretation
             of the scope entry is controlled by the outer aggregate, otherwise it is controlled by the inner aggregate.
             Example:
                  select a1
                  from {0} as a group by 
                                            anyelement(select value max(a + b) from {1} as b)
                                      as a1
             In this query the aggregate inside of a1 group key definition, the max(a + b), references scope entry a.
             Because a is referenced inside of the group key definition (which serves as an outer aggregate) and the key definition belongs to
             the same scope region as a, a is interpreted in the context of the group key definition, not the function aggregate and
             the expression for a is obtained by scopeEntry.GetExpression(...) call, not iGroupExpressionExtendedInfo.GroupVarBasedExpression.
            var expr = scopeEntry.GetExpression(varName, errCtx);
            Debug.Assert(expr != null, "scopeEntry.GetExpression(...) returned null");
            if (_currentGroupAggregateInfo != null)
            {
                 Make sure defining scope regions agree as described above.
                 Outer scope region has smaller index value than the inner.
                var definingScopeRegionOfScopeEntry = GetDefiningScopeRegion(scopeIndex);
                if (definingScopeRegionOfScopeEntry.ScopeRegionIndex
                    <= _currentGroupAggregateInfo.DefiningScopeRegion.ScopeRegionIndex)
                {
                     Let the group aggregate know the scope of the scope entry it references.
                     This affects the scope region that will evaluate the group aggregate.
                    _currentGroupAggregateInfo.UpdateScopeIndex(scopeIndex, this);
                    var iGroupExpressionExtendedInfo = scopeEntry as IGroupExpressionExtendedInfo;
                    if (iGroupExpressionExtendedInfo != null)
                    {
                         Find the aggregate that controls interpretation of the current scope entry.
                         This would be a containing aggregate with the defining scope region matching definingScopeRegionOfScopeEntry.
                         If there is no such aggregate, then the current containing aggregate controls interpretation.
                        GroupAggregateInfo expressionInterpretationContext;
                        for (expressionInterpretationContext = _currentGroupAggregateInfo;
                             expressionInterpretationContext != null &&
                             expressionInterpretationContext.DefiningScopeRegion.ScopeRegionIndex
                             >= definingScopeRegionOfScopeEntry.ScopeRegionIndex;
                             expressionInterpretationContext = expressionInterpretationContext.ContainingAggregate)
                        {
                            if (expressionInterpretationContext.DefiningScopeRegion.ScopeRegionIndex
                                == definingScopeRegionOfScopeEntry.ScopeRegionIndex)
                            {
                                break;
                            }
                        }
                        if (expressionInterpretationContext == null
                            ||
                            expressionInterpretationContext.DefiningScopeRegion.ScopeRegionIndex
                            < definingScopeRegionOfScopeEntry.ScopeRegionIndex)
                        {
                            expressionInterpretationContext = _currentGroupAggregateInfo;
                        }
                        switch (expressionInterpretationContext.AggregateKind)
                        {
                            case GroupAggregateKind.Function:
                                if (iGroupExpressionExtendedInfo.GroupVarBasedExpression != null)
                                {
                                    expr = iGroupExpressionExtendedInfo.GroupVarBasedExpression;
                                }
                                break;
                            case GroupAggregateKind.Partition:
                                if (iGroupExpressionExtendedInfo.GroupAggBasedExpression != null)
                                {
                                    expr = iGroupExpressionExtendedInfo.GroupAggBasedExpression;
                                }
                                break;
                            case GroupAggregateKind.GroupKey:
                                 User the current expression obtained from scopeEntry.GetExpression(...)
                                break;
                            default:
                                Debug.Fail("Unexpected group aggregate kind.");
                                break;
                        }
                    }
                }
            }
            return expr;
        }
private DbExpression GetExpressionFromScopeEntry ( ScopeEntry scopeEntry , int scopeIndex , string varName , ErrorContext errCtx ) { var expr = scopeEntry . GetExpression ( varName , errCtx ) ; Debug . Assert ( expr != null , " " ) ; if ( _currentGroupAggregateInfo != null ) { var definingScopeRegionOfScopeEntry = GetDefiningScopeRegion ( scopeIndex ) ; if ( definingScopeRegionOfScopeEntry . ScopeRegionIndex <= _currentGroupAggregateInfo . DefiningScopeRegion . ScopeRegionIndex ) { _currentGroupAggregateInfo . UpdateScopeIndex ( scopeIndex , this ) ; var iGroupExpressionExtendedInfo = scopeEntry as IGroupExpressionExtendedInfo ; if ( iGroupExpressionExtendedInfo != null ) { GroupAggregateInfo expressionInterpretationContext ; for ( expressionInterpretationContext = _currentGroupAggregateInfo ; expressionInterpretationContext != null && expressionInterpretationContext . DefiningScopeRegion . ScopeRegionIndex >= definingScopeRegionOfScopeEntry . ScopeRegionIndex ; expressionInterpretationContext = expressionInterpretationContext . ContainingAggregate ) { if ( expressionInterpretationContext . DefiningScopeRegion . ScopeRegionIndex == definingScopeRegionOfScopeEntry . ScopeRegionIndex ) { break ; } } if ( expressionInterpretationContext == null || expressionInterpretationContext . DefiningScopeRegion . ScopeRegionIndex < definingScopeRegionOfScopeEntry . ScopeRegionIndex ) { expressionInterpretationContext = _currentGroupAggregateInfo ; } switch ( expressionInterpretationContext . AggregateKind ) { case GroupAggregateKind . Function : if ( iGroupExpressionExtendedInfo . GroupVarBasedExpression != null ) { expr = iGroupExpressionExtendedInfo . GroupVarBasedExpression ; } break ; case GroupAggregateKind . Partition : if ( iGroupExpressionExtendedInfo . GroupAggBasedExpression != null ) { expr = iGroupExpressionExtendedInfo . GroupAggBasedExpression ; } break ; case GroupAggregateKind . GroupKey : break ; default : Debug . Fail ( " " ) ; break ; } } } } return expr ; }
Returns the appropriate expression from a given scope entry.
Returns the appropriate expression from a given scope entry .
// // If //      1) we are in the context of a group aggregate or group key,  //      2) and the scopeEntry can have multiple interpretations depending on the aggregation context, //      3) and the defining scope region of the scopeEntry is outer or equal to the defining scope region of the group aggregate, //      4) and the defining scope region of the scopeEntry is not performing conversion of a group key definition, // Then the expression that corresponds to the scopeEntry is either the GroupVarBasedExpression or the GroupAggBasedExpression. // Otherwise the default expression that corresponds to the scopeEntry is provided by scopeEntry.GetExpression(...) call. // // Explanation for #2 from the list above: // A scope entry may have multiple aggregation-context interpretations: //      - An expression in the context of a group key definition, obtained by scopeEntry.GetExpression(...); //        Example: select k1 from {0} as a group by a%2 as k1 //                                                  ^^^ //      - An expression in the context of a function aggregate, provided by iGroupExpressionExtendedInfo.GroupVarBasedExpression; //        Example: select max( a ) from {0} as a group by a%2 as k1 //                            ^^^ //      - An expression in the context of a group partition, provided by iGroupExpressionExtendedInfo.GroupAggBasedExpression; //        Example: select GroupPartition( a ) from {0} as a group by a%2 as k1 //                                       ^^^ // Note that expressions obtained from aggregation-context-dependent scope entries outside of the three contexts mentioned above // will default to the value returned by the scopeEntry.GetExpression(...) call. This value is the same as in the group key definition context. // These expressions have correct result types which enables partial expression validation.  // However the contents of the expressions are invalid outside of the group key definitions, hence they can not appear in the final expression tree. // SemanticAnalyzer.ProcessGroupByClause(...) method guarantees that such expressions are only temporarily used during GROUP BY clause processing and // dropped afterwards. // Example: select a, k1 from {0} as a group by a%2 as k1 //                 ^^^^^ - these expressions are processed twice: once during GROUP BY and then SELECT clause processing, //                         the expressions obtained during GROUP BY clause processing are dropped and only //                         the ones obtained during SELECT clause processing are accepted. // // Explanation for #3 from the list above: //      - An outer scope entry referenced inside of an aggregate may lift the aggregate to the outer scope region for evaluation,  //        hence such a scope entry must be interpreted in the aggregation context. See explanation for #4 below for more info. //        Example:  // //          select  //              (select max(x) from {1} as y)  //          from {0} as x // //      - If a scope entry is defined inside of a group aggregate, then the scope entry is not affected by the aggregate,  //        hence such a scope entry is not interpreted in the aggregation context. //        Example: // //          select max( //                       anyelement( select b from {1} as b )   //                    ) //          from {0} as a group by a %2 as a1 // //        In this query the aggregate argument contains a nested query expression. //        The nested query references b. Because b is defined inside of the aggregate it is not interpreted in the aggregation context and //        the expression for b should not be GroupVar/GroupAgg based, even though the reference to b appears inside of an aggregate. // // Explanation for #4 from the list above: // An aggregate evaluating on a particular scope region defines the interpretation of scope entries defined on that scope region. // In the case when an inner aggregate references a scope entry belonging to the evaluating region of an outer aggregate, the interpretation // of the scope entry is controlled by the outer aggregate, otherwise it is controlled by the inner aggregate. // Example: // //      select a1 //      from {0} as a group by  //                                anyelement(select value max(a + b) from {1} as b) //                          as a1 // // In this query the aggregate inside of a1 group key definition, the max(a + b), references scope entry a. // Because a is referenced inside of the group key definition (which serves as an outer aggregate) and the key definition belongs to // the same scope region as a, a is interpreted in the context of the group key definition, not the function aggregate and // the expression for a is obtained by scopeEntry.GetExpression(...) call, not iGroupExpressionExtendedInfo.GroupVarBasedExpression. // // // Make sure defining scope regions agree as described above. // Outer scope region has smaller index value than the inner. // // // Let the group aggregate know the scope of the scope entry it references. // This affects the scope region that will evaluate the group aggregate. // // // Find the aggregate that controls interpretation of the current scope entry. // This would be a containing aggregate with the defining scope region matching definingScopeRegionOfScopeEntry. // If there is no such aggregate, then the current containing aggregate controls interpretation. // // // User the current expression obtained from scopeEntry.GetExpression(...) //
scopeEntry
ScopeEntry
scopeIndex
int
varName
string
errCtx
ErrorContext