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 VisitGrouping(SqlGrouping g) { g.Key = this.VisitExpression(g.Key); g.Group = this.VisitExpression(g.Group); return g; }
private Type GenerateGrouping(SqlGrouping grp) { Type[] typeArgs = grp.ClrType.GetGenericArguments(); this.GenerateExpressionForType(grp.Key, typeArgs[0]); this.Generate(grp.Group); Type orbType = typeof(ObjectMaterializer<>).MakeGenericType(this.compiler.DataReaderType); MethodInfo miCreateGroup = TypeSystem.FindStaticMethod(orbType, "CreateGroup", new Type[] { typeArgs[0], typeof(IEnumerable<>).MakeGenericType(typeArgs[1]) }, typeArgs); Diagnostics.Debug.Assert(miCreateGroup != null); gen.Emit(OpCodes.Call, miCreateGroup); return miCreateGroup.ReturnType; }
internal override SqlExpression VisitGrouping(SqlGrouping g) { if(!_isDebugMode) { throw Error.InvalidFormatNode("Grouping"); } _commandStringBuilder.Append("Group("); this.Visit(g.Key); _commandStringBuilder.Append(", "); this.Visit(g.Group); _commandStringBuilder.Append(")"); return g; }
internal override SqlExpression VisitGrouping(SqlGrouping g) { SqlGrouping n = new SqlGrouping(g.ClrType, g.SqlType, this.VisitExpression(g.Key), this.VisitExpression(g.Group), g.SourceExpression ); return n; }
internal override SqlExpression VisitGrouping(SqlGrouping g) { g.Key = this.VisitNamedExpression(g.Key, "Key"); g.Group = this.VisitNamedExpression(g.Group, "Group"); return g; }
internal override SqlExpression VisitGrouping(SqlGrouping g) { g.Key = this.FetchExpression(g.Key); g.Group = this.FetchExpression(g.Group); return g; }