protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { var sequence = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0]) { CreateSubQuery = true }); var prevSequence = sequence; // Wrap by subquery to handle aggregate limitations, especially for SQL Server // sequence = new SubQueryContext(sequence); if (prevSequence.SelectQuery.OrderBy.Items.Count > 0) { if (prevSequence.SelectQuery.Select.TakeValue == null && prevSequence.SelectQuery.Select.SkipValue == null) { prevSequence.SelectQuery.OrderBy.Items.Clear(); } } var context = new AggregationContext(buildInfo.Parent, sequence, methodCall); var methodName = methodCall.Method.Name.Replace("Async", ""); var sql = sequence.ConvertToSql(null, 0, ConvertFlags.Field).Select(_ => _.Sql).ToArray(); if (sql.Length == 1) { if (sql[0] is SelectQuery query) { if (query.Select.Columns.Count == 1) { var join = query.OuterApply(); context.SelectQuery.From.Tables[0].Joins.Add(join.JoinedTable); sql[0] = query.Select.Columns[0]; } } } ISqlExpression sqlExpression = new SqlFunction(methodCall.Type, methodName, true, sql); if (sqlExpression == null) { throw new LinqToDBException("Invalid Aggregate function implementation"); } context.Sql = context.SelectQuery; context.FieldIndex = context.SelectQuery.Select.Add(sqlExpression, methodName); return(context); }
protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { var sequence = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0])); var query = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[1], new SelectQuery())); var except = query.SelectQuery; sequence = new SubQueryContext(sequence); var sql = sequence.SelectQuery; except.ParentSelect = sql; if (methodCall.Method.Name == "Except") { sql.Where.Not.Exists(except); } else { sql.Where.Exists(except); } var keys1 = sequence.ConvertToSql(null, 0, ConvertFlags.Key); var keys2 = query.ConvertToSql(null, 0, ConvertFlags.Key); if (keys1.Length != keys2.Length) { throw new InvalidOperationException(); } for (var i = 0; i < keys1.Length; i++) { except.Where .Expr(keys1[i].Sql) .Equal .Expr(keys2[i].Sql); } return(sequence); }
protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { var sequence1 = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0])); var sequence2 = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[1], new SelectQuery())); SetOperation setOperation; switch (methodCall.Method.Name) { case "Concat": case "UnionAll": setOperation = SetOperation.UnionAll; break; case "Union": setOperation = SetOperation.Union; break; case "Except": setOperation = SetOperation.Except; break; case "ExceptAll": setOperation = SetOperation.ExceptAll; break; case "Intersect": setOperation = SetOperation.Intersect; break; case "IntersectAll": setOperation = SetOperation.IntersectAll; break; default: throw new ArgumentException($"Invalid method name {methodCall.Method.Name}."); } var needsEmulation = !builder.DataContext.SqlProviderFlags.IsAllSetOperationsSupported && setOperation.In(SetOperation.ExceptAll, SetOperation.IntersectAll) || !builder.DataContext.SqlProviderFlags.IsDistinctSetOperationsSupported && setOperation.In(SetOperation.Except, SetOperation.Intersect); if (needsEmulation) { // emulation var sequence = new SubQueryContext(sequence1); var query = sequence2; var except = query.SelectQuery; var sql = sequence.SelectQuery; if (setOperation.In(SetOperation.Except, SetOperation.Intersect)) { sql.Select.IsDistinct = true; } except.ParentSelect = sql; if (setOperation.In(SetOperation.Except, SetOperation.ExceptAll)) { sql.Where.Not.Exists(except); } else { sql.Where.Exists(except); } var keys1 = sequence.ConvertToSql(null, 0, ConvertFlags.All); var keys2 = query.ConvertToSql(null, 0, ConvertFlags.All); if (keys1.Length != keys2.Length) { throw new InvalidOperationException(); } for (var i = 0; i < keys1.Length; i++) { except.Where .Expr(keys1[i].Sql) .Equal .Expr(keys2[i].Sql); } return(sequence); } var set1 = new SubQueryContext(sequence1); var set2 = new SubQueryContext(sequence2); var setOperator = new SqlSetOperator(set2.SelectQuery, setOperation); set1.SelectQuery.SetOperators.Add(setOperator); return(new SetOperationContext(set1, set2, methodCall)); }