internal override SqlExpression VisitMultiset(SqlSubSelect sms) { // allow one big-join per query? if ((this.options & Options.EnableBigJoin) != 0 && !this.hasBigJoin && this.canJoin && this.isTopLevel && this.outerSelect != null && !MultisetChecker.HasMultiset(sms.Select.Selection) && BigJoinChecker.CanBigJoin(sms.Select)) { sms.Select = this.VisitSelect(sms.Select); SqlAlias alias = new SqlAlias(sms.Select); SqlJoin join = new SqlJoin(SqlJoinType.OuterApply, this.outerSelect.From, alias, null, sms.SourceExpression); this.outerSelect.From = join; this.outerSelect.OrderingType = SqlOrderingType.Always; // make joined expression SqlExpression expr = (SqlExpression)SqlDuplicator.Copy(sms.Select.Selection); // make count expression SqlSelect copySelect = (SqlSelect)SqlDuplicator.Copy(sms.Select); SqlAlias copyAlias = new SqlAlias(copySelect); SqlSelect countSelect = new SqlSelect(sql.Unary(SqlNodeType.Count, null, sms.SourceExpression), copyAlias, sms.SourceExpression); countSelect.OrderingType = SqlOrderingType.Never; SqlExpression count = sql.SubSelect(SqlNodeType.ScalarSubSelect, countSelect); // make joined collection SqlJoinedCollection jc = new SqlJoinedCollection(sms.ClrType, sms.SqlType, expr, count, sms.SourceExpression); this.hasBigJoin = true; return(jc); } else { return(QueryExtractor.Extract(sms, this.parentParameters)); } }
private List <SqlExpression> GetIdentityExpressions(MetaType type, SqlExpression expr) { var list = GetIdentityMembers(type).ToList(); var list2 = new List <SqlExpression>(list.Count); foreach (var item in list) { list2.Add(sql.Member((SqlExpression)SqlDuplicator.Copy(expr), item)); } return(list2); }
private List <SqlExpression> GetIdentityExpressions(MetaType type, SqlExpression expr) { List <MetaDataMember> members = GetIdentityMembers(type).ToList(); System.Diagnostics.Debug.Assert(members.Count > 0); List <SqlExpression> exprs = new List <SqlExpression>(members.Count); foreach (MetaDataMember mm in members) { exprs.Add(sql.Member((SqlExpression)SqlDuplicator.Copy(expr), mm)); } return(exprs); }
private SqlExpression TranslateEqualsOp(SqlNodeType op, SqlExpression left, SqlExpression right, bool allowExpand) { switch (op) { case SqlNodeType.EQ: case SqlNodeType.NE: return(sql.Binary(op, left, right)); case SqlNodeType.EQ2V: if (SqlExpressionNullability.CanBeNull(left) != false && SqlExpressionNullability.CanBeNull(right) != false) { SqlNodeType eqOp = allowExpand ? SqlNodeType.EQ2V : SqlNodeType.EQ; return (sql.Binary(SqlNodeType.Or, sql.Binary(SqlNodeType.And, sql.Unary(SqlNodeType.IsNull, (SqlExpression)SqlDuplicator.Copy(left)), sql.Unary(SqlNodeType.IsNull, (SqlExpression)SqlDuplicator.Copy(right)) ), sql.Binary(SqlNodeType.And, sql.Binary(SqlNodeType.And, sql.Unary(SqlNodeType.IsNotNull, (SqlExpression)SqlDuplicator.Copy(left)), sql.Unary(SqlNodeType.IsNotNull, (SqlExpression)SqlDuplicator.Copy(right)) ), sql.Binary(eqOp, left, right) ) )); } else { SqlNodeType eqOp = allowExpand ? SqlNodeType.EQ2V : SqlNodeType.EQ; return(sql.Binary(eqOp, left, right)); } case SqlNodeType.NE2V: if (SqlExpressionNullability.CanBeNull(left) != false && SqlExpressionNullability.CanBeNull(right) != false) { SqlNodeType eqOp = allowExpand ? SqlNodeType.EQ2V : SqlNodeType.EQ; return (sql.Unary(SqlNodeType.Not, sql.Binary(SqlNodeType.Or, sql.Binary(SqlNodeType.And, sql.Unary(SqlNodeType.IsNull, (SqlExpression)SqlDuplicator.Copy(left)), sql.Unary(SqlNodeType.IsNull, (SqlExpression)SqlDuplicator.Copy(right)) ), sql.Binary(SqlNodeType.And, sql.Binary(SqlNodeType.And, sql.Unary(SqlNodeType.IsNotNull, (SqlExpression)SqlDuplicator.Copy(left)), sql.Unary(SqlNodeType.IsNotNull, (SqlExpression)SqlDuplicator.Copy(right)) ), sql.Binary(eqOp, left, right) ) ) )); } else { SqlNodeType neOp = allowExpand ? SqlNodeType.NE2V : SqlNodeType.NE; return(sql.Binary(neOp, left, right)); } default: throw Error.UnexpectedNode(op); } }
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 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 this.map[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); this.map[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 this.map[elemLambda.Parameters[0]] = selDupRef; elemExpr = this.VisitExpression(elemLambda.Body); // evaluate element expression relative to original sequence this.map[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, this.dominatingExpression); selElem.Where = sql.Binary(SqlNodeType.EQ2V, keyExpr, keyDup); // Finally, make the MULTISET node. this will be used as part of the final select SqlSubSelect ss = sql.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, this.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, this.typeProvider.From(elementType), keyExpr, ss, this.dominatingExpression); SqlSelect keyGroupSel = new SqlSelect(keyGroup, gselAlias, this.dominatingExpression); SqlAlias kgAlias = new SqlAlias(keyGroupSel); SqlAliasRef kgAliasRef = new SqlAliasRef(kgAlias); this.map[resultSelector.Parameters[0]] = sql.Member(kgAliasRef, elementType.GetProperty("Key")); this.map[resultSelector.Parameters[1]] = kgAliasRef; // remember the select that has the actual group (for optimizing aggregates later) this.gmap[kgAliasRef] = new GroupInfo { SelectWithGroup = gsel, ElementOnGroupSource = elemOnGroupSource }; SqlExpression resultExpr = this.VisitExpression(resultSelector.Body); result = new SqlSelect(resultExpr, kgAlias, this.dominatingExpression); // remember the select that has the actual group (for optimizing aggregates later) this.gmap[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, this.typeProvider.From(elementType), keyExpr, ss, this.dominatingExpression); result = new SqlSelect(resultExpr, gselAlias, this.dominatingExpression); // remember the select that has the actual group (for optimizing aggregates later) this.gmap[resultExpr] = new GroupInfo { SelectWithGroup = gsel, ElementOnGroupSource = elemOnGroupSource }; } return result; }
private SqlExpression MakeCoalesce(SqlExpression left, SqlExpression right, Type resultType) { CompensateForLowerPrecedenceOfDateType(ref left, ref right); // DevDiv 176874 if (TypeSystem.IsSimpleType(resultType)) { return sql.Binary(SqlNodeType.Coalesce, left, right, resultType); } else { List<SqlWhen> whens = new List<SqlWhen>(1); whens.Add(new SqlWhen(sql.Unary(SqlNodeType.IsNull, left, left.SourceExpression), right)); SqlDuplicator dup = new SqlDuplicator(true); return sql.SearchedCase(whens.ToArray(), (SqlExpression)dup.Duplicate(left), this.dominatingExpression); } }
private SqlNode VisitParameter(ParameterExpression p) { SqlExpression sqlExpr; if (this.map.TryGetValue(p, out sqlExpr)) return sqlExpr; Expression expr; if (this.exprMap.TryGetValue(p, out expr)) return this.Visit(expr); SqlNode nodeToDup; if (this.dupMap.TryGetValue(p, out nodeToDup)) { SqlDuplicator duplicator = new SqlDuplicator(true); return duplicator.Duplicate(nodeToDup); } throw Error.ParameterNotInScope(p.Name); }
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); }