internal SqlColumn BubbleUp(SqlColumn col, SqlNode source) { this.match = this.GetOriginatingColumn(col); this.found = null; this.Visit(source); return this.found; }
internal override SqlExpression VisitColumn(SqlColumn col) { if(col.Expression != null) { this.Visit(col.Expression); } return col; }
private static string GetColumnName(SqlColumn c) { #if DEBUG return c.Text; #else return c.Name; #endif }
internal SqlColumn GetOriginatingColumn(SqlColumn col) { SqlColumnRef cref = col.Expression as SqlColumnRef; if(cref != null) { return this.GetOriginatingColumn(cref.Column); } return col; }
internal override SqlExpression VisitColumn(SqlColumn col) { if(!String.IsNullOrEmpty(col.Name)) { this.Names.Add(col.Name); } return base.VisitColumn(col); }
private SqlColumnRef MakeFlattenedColumn(SqlExpression expr, string name) { SqlColumn c = (!this.isInput) ? this.FindColumnWithExpression(this.row.Columns, expr) : null; if(c == null) { c = new SqlColumn(expr.ClrType, expr.SqlType, name, null, expr, expr.SourceExpression); this.row.Columns.Add(c); } return new SqlColumnRef(c); }
internal override SqlExpression VisitColumnRef(SqlColumnRef cref) { SqlExpression result = base.VisitColumnRef(cref); if (result != null && result == cref) { // reference to outer scope, don't propogate references to expressions or aliases SqlColumn col = cref.Column; SqlColumn newcol = new SqlColumn(col.ClrType, col.SqlType, col.Name, col.MetaMember, null, col.SourceExpression); newcol.Ordinal = col.Ordinal; result = new SqlColumnRef(newcol); newcol.ClearSourceExpression(); } return result; }
internal override SqlRow VisitRow(SqlRow row) { for(int i = 0, n = row.Columns.Count; i < n; i++) { row.Columns[i].Expression = this.VisitExpression(row.Columns[i].Expression); if(this.hasRowNumber) { this.CurrentColumn = row.Columns[i]; break; } } return row; }
internal override SqlTable VisitTable(SqlTable tab) { foreach(SqlColumn c in tab.Columns) { if(c == this.match) { if(this.found != null) throw Error.ColumnIsDefinedInMultiplePlaces(GetColumnName(this.match)); this.found = c; break; } } return tab; }
internal override SqlRow VisitRow(SqlRow row) { foreach(SqlColumn c in row.Columns) { if(this.RefersToColumn(c, this.match)) { if(this.found != null) { throw Error.ColumnIsDefinedInMultiplePlaces(GetColumnName(this.match)); } this.found = c; break; } } return row; }
internal override SqlExpression VisitColumn(SqlColumn col) { SqlColumn c = this.FindColumn(this.row.Columns, col); if(c == null && col.Expression != null && !this.isInput && (!this.isNew || (this.isNew && !col.Expression.IsConstantColumn))) { c = this.FindColumnWithExpression(this.row.Columns, col.Expression); } if(c == null) { this.row.Columns.Add(col); c = col; } else if(c != col) { // preserve expr-sets when folding expressions together if(col.Expression.NodeType == SqlNodeType.ExprSet && c.Expression.NodeType != SqlNodeType.ExprSet) { c.Expression = col.Expression; } this.map[col] = c; } return new SqlColumnRef(c); }
internal override SqlAlias VisitAlias(SqlAlias a) { SqlTable tab = a.Node as SqlTable; SqlTableValuedFunctionCall tvf = a.Node as SqlTableValuedFunctionCall; if(this.addPrimaryKeys && (tab != null || tvf != null)) { List<SqlOrderExpression> list = new List<SqlOrderExpression>(); bool isTable = tab != null; MetaType rowType = isTable ? tab.RowType : tvf.RowType; foreach(MetaDataMember mm in rowType.IdentityMembers) { string name = mm.MappedName; SqlColumn col; Expression sourceExpression; List<SqlColumn> columns; if(isTable) { col = tab.Find(name); sourceExpression = tab.SourceExpression; columns = tab.Columns; } else { col = tvf.Find(name); sourceExpression = tvf.SourceExpression; columns = tvf.Columns; } if(col == null) { col = new SqlColumn(mm.MemberAccessor.Type, typeProvider.From(mm.MemberAccessor.Type), name, mm, null, sourceExpression); col.Alias = a; columns.Add(col); } list.Add(new SqlOrderExpression(SqlOrderType.Ascending, new SqlColumnRef(col))); } this.PrependOrderExpressions(list); return a; } else { return base.VisitAlias(a); } }
internal override SqlExpression VisitColumn(SqlColumn col) { VisitAliasConsumed(col.Alias); VisitExpression(col.Expression); return col; }
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; }
private void PushDown(SqlColumn column) { SqlSelect select = new SqlSelect(new SqlNop(column.ClrType, column.SqlType, column.SourceExpression), this.currentSelect.From, this.currentSelect.SourceExpression); this.currentSelect.From = new SqlAlias(select); select.Row.Columns.Add(column); }
internal override SqlExpression VisitColumn(SqlColumn col) { throw Error.UnexpectedFloatingColumn(); }
private SqlNode VisitAggregate(Expression sequence, LambdaExpression lambda, SqlNodeType aggType, Type returnType) { // Convert seq.Agg(exp) into // // 1) SELECT Agg(exp) FROM seq // 2) SELECT Agg1 FROM (SELECT Agg(exp) as Agg1 FROM group-seq GROUP BY ...) // 3) SCALAR(SELECT Agg(exp) FROM seq) // bool isCount = aggType == SqlNodeType.Count || aggType == SqlNodeType.LongCount; SqlNode source = this.Visit(sequence); SqlSelect select = this.CoerceToSequence(source); SqlAlias alias = new SqlAlias(select); SqlAliasRef aref = new SqlAliasRef(alias); // If the sequence is of the form x.Select(expr).Agg() and the lambda for the aggregate is null, // or is a no-op parameter expression (like u=>u), clone the group by selection lambda // expression, and use for the aggregate. // Final form should be x.Agg(expr) MethodCallExpression mce = sequence as MethodCallExpression; if(!_outerNode && !isCount && (lambda == null || (lambda.Parameters.Count == 1 && lambda.Parameters[0] == lambda.Body)) && (mce != null) && IsSequenceOperatorCall(mce, "Select") && select.From is SqlAlias) { LambdaExpression selectionLambda = GetLambda(mce.Arguments[1]); lambda = Expression.Lambda(selectionLambda.Type, selectionLambda.Body, selectionLambda.Parameters); alias = (SqlAlias)select.From; aref = new SqlAliasRef(alias); } if(lambda != null && !TypeSystem.IsSimpleType(lambda.Body.Type)) { throw Error.CannotAggregateType(lambda.Body.Type); } //Empty parameter aggregates are not allowed on anonymous types //i.e. db.Customers.Select(c=>new{c.Age}).Max() instead it should be // db.Customers.Select(c=>new{c.Age}).Max(c=>c.Age) if(select.Selection.SqlType.IsRuntimeOnlyType && !IsGrouping(sequence.Type) && !isCount && lambda == null) { throw Error.NonCountAggregateFunctionsAreNotValidOnProjections(aggType); } if(lambda != null) _parameterExpressionToSqlExpression[lambda.Parameters[0]] = aref; if(_outerNode) { // If this aggregate is basically the last/outer-most operator of the query // // produce SELECT Agg(exp) FROM seq // SqlExpression exp = (lambda != null) ? this.VisitExpression(lambda.Body) : null; SqlExpression where = null; if(isCount && exp != null) { where = exp; exp = null; } else if(exp == null && !isCount) { exp = aref; } if(exp != null) { // in case this contains another aggregate exp = new SqlSimpleExpression(exp); } SqlSelect sel = new SqlSelect( this.GetAggregate(aggType, returnType, exp), alias, _dominatingExpression ); sel.Where = where; sel.OrderingType = SqlOrderingType.Never; return sel; } else if(!isCount || lambda == null) { // Look to optimize aggregate by pushing its evaluation down to the select node that has the // actual group-by operator. // // Produce: SELECT Agg1 FROM (SELECT Agg(exp) as Agg1 FROM seq GROUP BY ...) // GroupInfo info = this.FindGroupInfo(source); if(info != null) { SqlExpression exp = null; if(lambda != null) { // evaluate expression relative to the group-by select node _parameterExpressionToSqlExpression[lambda.Parameters[0]] = (SqlExpression)SqlDuplicator.Copy(info.ElementOnGroupSource); exp = this.VisitExpression(lambda.Body); } else if(!isCount) { // support aggregates w/o an explicit selector specified exp = info.ElementOnGroupSource; } if(exp != null) { // in case this contains another aggregate exp = new SqlSimpleExpression(exp); } SqlExpression agg = this.GetAggregate(aggType, returnType, exp); SqlColumn c = new SqlColumn(agg.ClrType, agg.SqlType, null, null, agg, _dominatingExpression); info.SelectWithGroup.Row.Columns.Add(c); return new SqlColumnRef(c); } } // Otherwise, if we cannot optimize then fall back to generating a nested aggregate in a correlated sub query // // SCALAR(SELECT Agg(exp) FROM seq) { SqlExpression exp = (lambda != null) ? this.VisitExpression(lambda.Body) : null; if(exp != null) { // in case this contains another aggregate exp = new SqlSimpleExpression(exp); } SqlSelect sel = new SqlSelect( this.GetAggregate(aggType, returnType, isCount ? null : (lambda == null) ? aref : exp), alias, _dominatingExpression ); sel.Where = isCount ? exp : null; return _nodeFactory.SubSelect(SqlNodeType.ScalarSubSelect, sel); } }
internal SqlColumnRef(SqlColumn col) : base(SqlNodeType.ColumnRef, col.ClrType, col.SourceExpression) { this.column = col; }
private void ForceLocal(SqlRow row, string name) { bool isLocal = false; // check to see if it already exists locally foreach(SqlColumn c in row.Columns) { if(this.RefersToColumn(c, this.found)) { this.found = c; isLocal = true; break; } } if(!isLocal) { // need to put this in the local projection list to bubble it up SqlColumn c = new SqlColumn(found.ClrType, found.SqlType, name, this.found.MetaMember, new SqlColumnRef(this.found), row.SourceExpression); row.Columns.Add(c); this.found = c; } }
internal virtual SqlExpression VisitColumn(SqlColumn col) { col.Expression = this.VisitExpression(col.Expression); return col; }
private bool IsUniqueName(List<SqlColumn> columns, ICollection<string> reservedNames, SqlColumn c, string name) { foreach(SqlColumn sc in columns) { if(sc != c && String.Compare(sc.Name, name, StringComparison.OrdinalIgnoreCase) == 0) return false; } if(!IsSimpleColumn(c, name)) { return !reservedNames.Contains(name); } return true; }
internal override SqlExpression VisitColumn(SqlColumn c) { if(!_isDebugMode) { throw Error.InvalidFormatNode("Column"); } _commandStringBuilder.Append("COLUMN("); if(c.Expression != null) { this.Visit(c.Expression); } else { string aliasName = null; if(c.Alias != null) { if(c.Alias.Name == null) { if(!_names.TryGetValue(c.Alias, out aliasName)) { aliasName = "A" + _names.Count; _names[c.Alias] = aliasName; } } else { aliasName = c.Alias.Name; } } _commandStringBuilder.Append(aliasName); _commandStringBuilder.Append("."); _commandStringBuilder.Append(c.Name); } _commandStringBuilder.Append(")"); return c; }
internal override SqlExpression VisitColumn(SqlColumn col) { SqlColumn n = new SqlColumn(col.ClrType, col.SqlType, col.Name, col.MetaMember, null, col.SourceExpression); this.nodeMap[col] = n; n.Expression = this.VisitExpression(col.Expression); n.Alias = (SqlAlias)this.Visit(col.Alias); return n; }
/// <summary> /// An expression is a simple reprojection if it's a column node whose expression is null, or /// whose expression is a column whose name matches the name of the given name or where /// where the given name is null or empty. /// </summary> /// <param name="c"></param> /// <returns></returns> private static bool IsSimpleColumn(SqlColumn c, string name) { if(c.Expression != null) { switch(c.Expression.NodeType) { case SqlNodeType.ColumnRef: var colRef = c.Expression as SqlColumnRef; return String.IsNullOrEmpty(name) || String.Compare(name, colRef.Column.Name, StringComparison.OrdinalIgnoreCase) == 0; default: return false; } } return true; }
internal bool RefersToColumn(SqlExpression exp, SqlColumn col) { #if DEBUG try { refersDepth++; System.Diagnostics.Debug.Assert(refersDepth < 20); #endif if (exp != null) { switch (exp.NodeType) { case SqlNodeType.Column: return exp == col || this.RefersToColumn(((SqlColumn)exp).Expression, col); case SqlNodeType.ColumnRef: SqlColumnRef cref = (SqlColumnRef)exp; return cref.Column == col || this.RefersToColumn(cref.Column.Expression, col); case SqlNodeType.ExprSet: SqlExprSet set = (SqlExprSet)exp; for (int i = 0, n = set.Expressions.Count; i < n; i++) { if (this.RefersToColumn(set.Expressions[i], col)) { return true; } } break; case SqlNodeType.OuterJoinedValue: return this.RefersToColumn(((SqlUnary)exp).Operand, col); } } return false; #if DEBUG } finally { refersDepth--; } #endif }
internal override SqlExpression VisitColumn(SqlColumn col) { _aliasMap[col] = _currentAlias; this.Visit(col.Expression); return col; }
internal override SqlExpression VisitColumn(SqlColumn col) { return new SqlColumnRef(col); }
private SqlSelect GenerateSkipTake(SqlSelect sequence, SqlExpression skipExp, SqlExpression takeExp) { SqlSelect select = this.LockSelect(sequence); // no skip? if(skipExp == null) { if(takeExp != null) { select.Top = takeExp; } return select; } SqlAlias alias = new SqlAlias(select); SqlAliasRef aref = new SqlAliasRef(alias); if(this.UseConverterStrategy(ConverterStrategy.SkipWithRowNumber)) { // use ROW_NUMBER() (preferred) SqlColumn rowNumber = new SqlColumn("ROW_NUMBER", _nodeFactory.RowNumber(new List<SqlOrderExpression>(), _dominatingExpression)); SqlColumnRef rowNumberRef = new SqlColumnRef(rowNumber); select.Row.Columns.Add(rowNumber); SqlSelect final = new SqlSelect(aref, alias, _dominatingExpression); if(takeExp != null) { // use BETWEEN for skip+take combo (much faster) final.Where = _nodeFactory.Between( rowNumberRef, _nodeFactory.Add(skipExp, 1), _nodeFactory.Binary(SqlNodeType.Add, (SqlExpression)SqlDuplicator.Copy(skipExp), takeExp), _dominatingExpression ); } else { final.Where = _nodeFactory.Binary(SqlNodeType.GT, rowNumberRef, skipExp); } return final; } else { // Ensure that the sequence contains elements that can be skipped if(!CanSkipOnSelection(select.Selection)) { throw Error.SkipNotSupportedForSequenceTypes(); } // use NOT EXISTS // Supported cases: // - Entities // - Projections that contain all PK columns // // .. where there sequence can be traced back to a: // - Single-table query // - Distinct // - Except // - Intersect // - Union, where union.All == false // Not supported: joins // Sequence should also be ordered, but we can't test for it at this // point in processing, and we won't know that we need to test it, later. SingleTableQueryVisitor stqv = new SingleTableQueryVisitor(); stqv.Visit(select); if(!stqv.IsValid) { throw Error.SkipRequiresSingleTableQueryWithPKs(); } SqlSelect dupsel = (SqlSelect)SqlDuplicator.Copy(select); dupsel.Top = skipExp; SqlAlias dupAlias = new SqlAlias(dupsel); SqlAliasRef dupRef = new SqlAliasRef(dupAlias); SqlSelect eqsel = new SqlSelect(dupRef, dupAlias, _dominatingExpression); eqsel.Where = _nodeFactory.Binary(SqlNodeType.EQ2V, aref, dupRef); SqlSubSelect ss = _nodeFactory.SubSelect(SqlNodeType.Exists, eqsel); SqlSelect final = new SqlSelect(aref, alias, _dominatingExpression); final.Where = _nodeFactory.Unary(SqlNodeType.Not, ss, _dominatingExpression); final.Top = takeExp; return final; } }
private SqlColumn FindColumn(IEnumerable<SqlColumn> columns, SqlColumn col) { foreach(SqlColumn c in columns) { if(this.RefersToColumn(c, col)) { return c; } } return null; }
internal override SqlExpression VisitTableValuedFunctionCall(SqlTableValuedFunctionCall fc) { foreach(SqlColumn c in fc.Columns) { if(c == this.match) { if(this.found != null) throw Error.ColumnIsDefinedInMultiplePlaces(GetColumnName(this.match)); this.found = c; break; } } return fc; }