private SqlNode VisitGroupBy(Expression sequence, LambdaExpression keyLambda, LambdaExpression elemLambda, LambdaExpression resultSelector) { // Convert seq.Group(elem, key) into // // SELECT s.key, MULTISET(select s2.elem from seq AS s2 where s.key == s2.key) // FROM seq AS s // // where key and elem can be either simple scalars or object constructions // SqlSelect seq = this.VisitSequence(sequence); seq = this.LockSelect(seq); SqlAlias seqAlias = new SqlAlias(seq); SqlAliasRef seqAliasRef = new SqlAliasRef(seqAlias); // evaluate the key expression relative to original sequence _parameterExpressionToSqlExpression[keyLambda.Parameters[0]] = seqAliasRef; SqlExpression keyExpr = this.VisitExpression(keyLambda.Body); // make a duplicate of the original sequence to use as a foundation of our group multiset SqlDuplicator sd = new SqlDuplicator(); SqlSelect selDup = (SqlSelect)sd.Duplicate(seq); // rebind key in relative to the duplicate sequence SqlAlias selDupAlias = new SqlAlias(selDup); SqlAliasRef selDupRef = new SqlAliasRef(selDupAlias); _parameterExpressionToSqlExpression[keyLambda.Parameters[0]] = selDupRef; SqlExpression keyDup = this.VisitExpression(keyLambda.Body); SqlExpression elemExpr = null; SqlExpression elemOnGroupSource = null; if(elemLambda != null) { // evaluate element expression relative to the duplicate sequence _parameterExpressionToSqlExpression[elemLambda.Parameters[0]] = selDupRef; elemExpr = this.VisitExpression(elemLambda.Body); // evaluate element expression relative to original sequence _parameterExpressionToSqlExpression[elemLambda.Parameters[0]] = seqAliasRef; elemOnGroupSource = this.VisitExpression(elemLambda.Body); } else { // no elem expression supplied, so just use an alias ref to the duplicate sequence. // this will resolve to whatever was being produced by the sequence elemExpr = selDupRef; elemOnGroupSource = seqAliasRef; } // Make a sub expression out of the key. This will allow a single definition of the // expression to be shared at multiple points in the tree (via SqlSharedExpressionRef's) SqlSharedExpression keySubExpr = new SqlSharedExpression(keyExpr); keyExpr = new SqlSharedExpressionRef(keySubExpr); // construct the select clause that picks out the elements (this may be redundant...) SqlSelect selElem = new SqlSelect(elemExpr, selDupAlias, _dominatingExpression); selElem.Where = _nodeFactory.Binary(SqlNodeType.EQ2V, keyExpr, keyDup); // Finally, make the MULTISET node. this will be used as part of the final select SqlSubSelect ss = _nodeFactory.SubSelect(SqlNodeType.Multiset, selElem); // add a layer to the original sequence before applying the actual group-by clause SqlSelect gsel = new SqlSelect(new SqlSharedExpressionRef(keySubExpr), seqAlias, _dominatingExpression); gsel.GroupBy.Add(keySubExpr); SqlAlias gselAlias = new SqlAlias(gsel); SqlSelect result = null; if(resultSelector != null) { // Create final select to include construction of group multiset // select new Grouping { Key = key, Group = Multiset(select elem from seq where match) } from ... Type elementType = typeof(IGrouping<,>).MakeGenericType(keyExpr.ClrType, elemExpr.ClrType); SqlExpression keyGroup = new SqlGrouping(elementType, _typeProvider.From(elementType), keyExpr, ss, _dominatingExpression); SqlSelect keyGroupSel = new SqlSelect(keyGroup, gselAlias, _dominatingExpression); SqlAlias kgAlias = new SqlAlias(keyGroupSel); SqlAliasRef kgAliasRef = new SqlAliasRef(kgAlias); _parameterExpressionToSqlExpression[resultSelector.Parameters[0]] = _nodeFactory.Member(kgAliasRef, elementType.GetProperty("Key")); _parameterExpressionToSqlExpression[resultSelector.Parameters[1]] = kgAliasRef; // remember the select that has the actual group (for optimizing aggregates later) _sqlNodeToGroupInfo[kgAliasRef] = new GroupInfo { SelectWithGroup = gsel, ElementOnGroupSource = elemOnGroupSource }; SqlExpression resultExpr = this.VisitExpression(resultSelector.Body); result = new SqlSelect(resultExpr, kgAlias, _dominatingExpression); // remember the select that has the actual group (for optimizing aggregates later) _sqlNodeToGroupInfo[resultExpr] = new GroupInfo { SelectWithGroup = gsel, ElementOnGroupSource = elemOnGroupSource }; } else { // Create final select to include construction of group multiset // select new Grouping { Key = key, Group = Multiset(select elem from seq where match) } from ... Type elementType = typeof(IGrouping<,>).MakeGenericType(keyExpr.ClrType, elemExpr.ClrType); SqlExpression resultExpr = new SqlGrouping(elementType, _typeProvider.From(elementType), keyExpr, ss, _dominatingExpression); result = new SqlSelect(resultExpr, gselAlias, _dominatingExpression); // remember the select that has the actual group (for optimizing aggregates later) _sqlNodeToGroupInfo[resultExpr] = new GroupInfo { SelectWithGroup = gsel, ElementOnGroupSource = elemOnGroupSource }; } return result; }
internal virtual SqlExpression VisitSharedExpression(SqlSharedExpression shared) { shared.Expression = this.VisitExpression(shared.Expression); return shared; }
internal SqlSharedExpressionRef(SqlSharedExpression expr) : base(SqlNodeType.SharedExpressionRef, expr.ClrType, expr.SourceExpression) { this.expr = expr; }
internal override SqlExpression VisitSharedExpression(SqlSharedExpression shared) { return this.VisitExpression(shared.Expression); }
internal override SqlExpression VisitSharedExpression(SqlSharedExpression shared) { if(!_isDebugMode) { throw Error.InvalidFormatNode("Shared"); } _commandStringBuilder.Append("SHARED("); this.Visit(shared.Expression); _commandStringBuilder.Append(")"); return shared; }
internal override SqlExpression VisitSharedExpression(SqlSharedExpression sub) { SqlSharedExpression n = new SqlSharedExpression(sub.Expression); this.nodeMap[sub] = n; n.Expression = this.VisitExpression(sub.Expression); return n; }
internal override SqlExpression VisitSharedExpression(SqlSharedExpression shared) { throw Error.UnexpectedSharedExpression(); }
internal override SqlExpression VisitSharedExpression(SqlSharedExpression shared) { shared.Expression = this.VisitExpression(shared.Expression); // shared expressions in group-by/select must be only column refs if (shared.Expression.NodeType == SqlNodeType.ColumnRef) { return shared.Expression; } else { // not simple? better push it down (make a sub-select that projects the relevant bits shared.Expression = this.PushDownExpression(shared.Expression); return shared.Expression; } }