예제 #1
0
 internal virtual SqlExpression VisitSimpleExpression(SqlSimpleExpression simple) {
     simple.Expression = this.VisitExpression(simple.Expression);
     return simple;
 }
예제 #2
0
		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);
			}
		}
예제 #3
0
		internal override SqlExpression VisitSimpleExpression(SqlSimpleExpression simple)
		{
			SqlSimpleExpression n = new SqlSimpleExpression(this.VisitExpression(simple.Expression));
			return n;
		}
예제 #4
0
		internal override SqlExpression VisitSimpleExpression(SqlSimpleExpression simple)
		{
			if(!_isDebugMode)
			{
				throw Error.InvalidFormatNode("SIMPLE");
			}
			_commandStringBuilder.Append("SIMPLE(");
			base.VisitSimpleExpression(simple);
			_commandStringBuilder.Append(")");
			return simple;
		}
예제 #5
0
		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;
		}