internal virtual SqlExpression VisitSimpleExpression(SqlSimpleExpression simple) { simple.Expression = this.VisitExpression(simple.Expression); return simple; }
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 override SqlExpression VisitSimpleExpression(SqlSimpleExpression simple) { SqlSimpleExpression n = new SqlSimpleExpression(this.VisitExpression(simple.Expression)); return n; }
internal override SqlExpression VisitSimpleExpression(SqlSimpleExpression simple) { if(!_isDebugMode) { throw Error.InvalidFormatNode("SIMPLE"); } _commandStringBuilder.Append("SIMPLE("); base.VisitSimpleExpression(simple); _commandStringBuilder.Append(")"); return simple; }
internal override SqlExpression VisitSimpleExpression(SqlSimpleExpression simple) { simple.Expression = this.VisitExpression(simple.Expression); if (SimpleExpression.IsSimple(simple.Expression)) { return simple.Expression; } SqlExpression result = this.PushDownExpression(simple.Expression); // simple expressions must be scalar (such that they can be formed into a single column declaration) Diagnostics.Debug.Assert(result is SqlColumnRef); return result; }