internal override SqlRowNumber VisitRowNumber(SqlRowNumber rowNumber) { if(rowNumber.OrderBy.Count > 0) return rowNumber; SqlDuplicator dup = new SqlDuplicator(true); List<SqlOrderExpression> orderBy = new List<SqlOrderExpression>(); List<SqlOrderExpression> existingOrders = new List<SqlOrderExpression>(); if(this.rowNumberOrders != null && this.rowNumberOrders.Count != 0) { existingOrders = new List<SqlOrderExpression>(this.rowNumberOrders); } else if(this.orders != null) { existingOrders = new List<SqlOrderExpression>(this.orders); } foreach(SqlOrderExpression expr in existingOrders) { if(!expr.Expression.IsConstantColumn) { orderBy.Add(expr); if(this.rowNumberOrders != null) { this.rowNumberOrders.Remove(expr); } if(this.orders != null) { this.orders.Remove(expr); } } } rowNumber.OrderBy.Clear(); if(orderBy.Count == 0) { List<SqlColumn> columns = SqlGatherColumnsProduced.GatherColumns(this.currentSelect.From); foreach(SqlColumn col in columns) { if(col.Expression.SqlType.IsOrderable) { orderBy.Add(new SqlOrderExpression(SqlOrderType.Ascending, new SqlColumnRef(col))); } } if(orderBy.Count == 0) { // insert simple column SqlColumn col = new SqlColumn( "rowNumberOrder", sql.Value(typeof(int), this.typeProvider.From(typeof(int)), 1, false, rowNumber.SourceExpression) ); this.PushDown(col); orderBy.Add(new SqlOrderExpression(SqlOrderType.Ascending, new SqlColumnRef(col))); } } foreach(SqlOrderExpression sox in orderBy) { rowNumber.OrderBy.Add(new SqlOrderExpression(sox.OrderType, (SqlExpression)dup.Duplicate(sox.Expression))); } return rowNumber; }
internal override SqlSelect VisitSelect(SqlSelect select) { bool saveTop = this.topSelect; bool savePK = this.addPrimaryKeys; SqlSelect saveSelect = this.currentSelect; this.currentSelect = select; if(select.OrderingType == SqlOrderingType.Always) { this.addPrimaryKeys = true; } this.topSelect = false; // can't forward ordering information through a group-by if(select.GroupBy.Count > 0) { this.Visit(select.From); this.orders = null; } else { this.Visit(select.From); } if(select.OrderBy.Count > 0) { this.PrependOrderExpressions(select.OrderBy); } List<SqlOrderExpression> save = this.orders; this.orders = null; this.rowNumberOrders = save; // lest orders be null when we need info /* do all the lower level stuff */ select.Where = this.VisitExpression(select.Where); for(int i = 0, n = select.GroupBy.Count; i < n; i++) { select.GroupBy[i] = this.VisitExpression(select.GroupBy[i]); } select.Having = this.VisitExpression(select.Having); for(int i = 0, n = select.OrderBy.Count; i < n; i++) { select.OrderBy[i].Expression = this.VisitExpression(select.OrderBy[i].Expression); } select.Top = this.VisitExpression(select.Top); select.Selection = this.VisitExpression(select.Selection); select.Row = (SqlRow)this.Visit(select.Row); this.topSelect = saveTop; this.addPrimaryKeys = savePK; this.orders = save; // all ordering is blocked for this layer and above if(select.OrderingType == SqlOrderingType.Blocked) { this.orders = null; } // rebuild orderby expressions, provided this select doesn't contain a SqlRowNumber // otherwise, replace the orderby with a reference to that column select.OrderBy.Clear(); var rowNumberChecker = new SqlRowNumberChecker(); if(rowNumberChecker.HasRowNumber(select) && rowNumberChecker.RowNumberColumn != null) { select.Row.Columns.Remove(rowNumberChecker.RowNumberColumn); this.PushDown(rowNumberChecker.RowNumberColumn); this.Orders.Add(new SqlOrderExpression(SqlOrderType.Ascending, new SqlColumnRef(rowNumberChecker.RowNumberColumn))); } if((this.topSelect || select.Top != null) && select.OrderingType != SqlOrderingType.Never && this.orders != null) { this.orders = new HashSet<SqlOrderExpression>(this.orders).ToList(); SqlDuplicator dup = new SqlDuplicator(true); foreach(SqlOrderExpression sox in this.orders) { select.OrderBy.Add(new SqlOrderExpression(sox.OrderType, (SqlExpression)dup.Duplicate(sox.Expression))); } } this.currentSelect = saveSelect; return select; }
private SqlExpression MakeCoalesce(SqlExpression left, SqlExpression right, Type resultType) { CompensateForLowerPrecedenceOfDateType(ref left, ref right); // DevDiv 176874 if(TypeSystem.IsSimpleType(resultType)) { return _nodeFactory.Binary(SqlNodeType.Coalesce, left, right, resultType); } else { List<SqlWhen> whens = new List<SqlWhen>(1); whens.Add(new SqlWhen(_nodeFactory.Unary(SqlNodeType.IsNull, left, left.SourceExpression), right)); SqlDuplicator dup = new SqlDuplicator(true); return _nodeFactory.SearchedCase(whens.ToArray(), (SqlExpression)dup.Duplicate(left), _dominatingExpression); } }
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; }
private SqlNode VisitParameter(ParameterExpression p) { SqlExpression sqlExpr; if(_parameterExpressionToSqlExpression.TryGetValue(p, out sqlExpr)) return sqlExpr; Expression expr; if(_parameterExpressionToExpression.TryGetValue(p, out expr)) return this.Visit(expr); SqlNode nodeToDup; if(_parameterExpressionToSqlNode.TryGetValue(p, out nodeToDup)) { SqlDuplicator duplicator = new SqlDuplicator(true); return duplicator.Duplicate(nodeToDup); } throw Error.ParameterNotInScope(p.Name); }