Ejemplo n.º 1
0
        protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo)
        {
            var sequence           = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0]));
            var collectionSelector = (LambdaExpression)methodCall.Arguments[1].Unwrap();
            var resultSelector     = (LambdaExpression)methodCall.Arguments[2].Unwrap();

            if (sequence.SelectQuery.HasUnion || !sequence.SelectQuery.IsSimple)
            {
                sequence = new SubQueryContext(sequence);
            }

            var context = new SelectManyContext(buildInfo.Parent, collectionSelector, sequence);
            var expr    = collectionSelector.Body.Unwrap();

            var collectionInfo = new BuildInfo(context, expr, new SelectQuery());
            var collection     = builder.BuildSequence(collectionInfo);
            var leftJoin       = collection is DefaultIfEmptyBuilder.DefaultIfEmptyContext || collectionInfo.JoinType == JoinType.Left;
            var sql            = collection.SelectQuery;

            var sequenceTables = new HashSet <ISqlTableSource>(sequence.SelectQuery.From.Tables[0].GetTables());
            var newQuery       = null != QueryVisitor.Find(sql, e => e == collectionInfo.SelectQuery);
            var crossApply     = null != QueryVisitor.Find(sql, e =>
                                                           e.ElementType == QueryElementType.TableSource && sequenceTables.Contains((ISqlTableSource)e) ||
                                                           e.ElementType == QueryElementType.SqlField && sequenceTables.Contains(((SqlField)e).Table) ||
                                                           e.ElementType == QueryElementType.Column && sequenceTables.Contains(((SqlColumn)e).Parent));

            if (collection is JoinBuilder.GroupJoinSubQueryContext queryContext)
            {
                var groupJoin = queryContext.GroupJoin;

                groupJoin.SelectQuery.From.Tables[0].Joins[0].JoinType = JoinType.Inner;
                groupJoin.SelectQuery.From.Tables[0].Joins[0].IsWeak   = false;
            }

            if (!newQuery)
            {
                if (collection.SelectQuery.Select.HasModifier)
                {
                    if (crossApply)
                    {
                        var foundJoin = context.SelectQuery.FindJoin(j => j.Table.Source == collection.SelectQuery);
                        if (foundJoin != null)
                        {
                            foundJoin.JoinType = leftJoin ? JoinType.OuterApply : JoinType.CrossApply;

                            collection.SelectQuery.Where.ConcatSearchCondition(foundJoin.Condition);

                            ((ISqlExpressionWalkable)collection.SelectQuery.Where).Walk(false, e =>
                            {
                                if (e is SqlColumn column)
                                {
                                    if (column.Parent == collection.SelectQuery)
                                    {
                                        return(column.UnderlyingColumn);
                                    }
                                }
                                return(e);
                            });

                            foundJoin.Condition.Conditions.Clear();
                        }
                    }
                }

                context.Collection = new SubQueryContext(collection, sequence.SelectQuery, false);
                return(new SelectContext(buildInfo.Parent, resultSelector, sequence, context));
            }

            if (!crossApply)
            {
                if (!leftJoin)
                {
                    context.Collection = new SubQueryContext(collection, sequence.SelectQuery, true);
                    return(new SelectContext(buildInfo.Parent, resultSelector, sequence, context));
                }
                else
                {
                    var join = sql.OuterApply();
                    sequence.SelectQuery.From.Tables[0].Joins.Add(join.JoinedTable);
                    context.Collection = new SubQueryContext(collection, sequence.SelectQuery, false);

                    return(new SelectContext(buildInfo.Parent, resultSelector, sequence, context));
                }
            }

            void MoveSearchConditionsToJoin(SqlFromClause.Join join)
            {
                var tableSources = new HashSet <ISqlTableSource>();

                ((ISqlExpressionWalkable)sql.Where.SearchCondition).Walk(false, e =>
                {
                    if (e is ISqlTableSource ts && !tableSources.Contains(ts))
                    {
                        tableSources.Add(ts);
                    }
                    return(e);
                });

                bool ContainsTable(ISqlTableSource tbl, IQueryElement qe)
                {
                    return(null != QueryVisitor.Find(qe, e =>
                                                     e == tbl ||
                                                     e.ElementType == QueryElementType.SqlField && tbl == ((SqlField)e).Table ||
                                                     e.ElementType == QueryElementType.Column && tbl == ((SqlColumn)e).Parent));
                }

                var conditions = sql.Where.SearchCondition.Conditions;

                if (conditions.Count > 0)
                {
                    for (var i = conditions.Count - 1; i >= 0; i--)
                    {
                        var condition = conditions[i];

                        if (!tableSources.Any(ts => ContainsTable(ts, condition)))
                        {
                            join.JoinedTable.Condition.Conditions.Insert(0, condition);
                            conditions.RemoveAt(i);
                        }
                    }
                }
            }
        static IBuildContext BuildCteContext(ExpressionBuilder builder, BuildInfo buildInfo)
        {
            var methodCall = (MethodCallExpression)buildInfo.Expression;

            Expression bodyExpr;
            IQueryable?query       = null;
            string?    name        = null;
            bool       isRecursive = false;

            switch (methodCall.Arguments.Count)
            {
            case 1:
                bodyExpr = methodCall.Arguments[0].Unwrap();
                break;

            case 2:
                bodyExpr = methodCall.Arguments[0].Unwrap();
                name     = methodCall.Arguments[1].EvaluateExpression() as string;
                break;

            case 3:
                query       = methodCall.Arguments[0].EvaluateExpression() as IQueryable;
                bodyExpr    = methodCall.Arguments[1].Unwrap();
                name        = methodCall.Arguments[2].EvaluateExpression() as string;
                isRecursive = true;
                break;

            default:
                throw new InvalidOperationException();
            }

            bodyExpr = builder.ConvertExpression(bodyExpr);
            builder.RegisterCte(query, bodyExpr, () => new CteClause(null, bodyExpr.Type.GetGenericArguments()[0], isRecursive, name));

            var cte = builder.BuildCte(bodyExpr,
                                       cteClause =>
            {
                var info     = new BuildInfo(buildInfo, bodyExpr, new SelectQuery());
                var sequence = builder.BuildSequence(info);

                if (cteClause == null)
                {
                    cteClause = new CteClause(sequence.SelectQuery, bodyExpr.Type.GetGenericArguments()[0], isRecursive, name);
                }
                else
                {
                    cteClause.Body = sequence.SelectQuery;
                    cteClause.Name = name;
                }

                return(Tuple.Create(cteClause, (IBuildContext?)sequence));
            }
                                       );

            var cteBuildInfo = new BuildInfo(buildInfo, bodyExpr, buildInfo.SelectQuery);
            var cteContext   = new CteTableContext(builder, cteBuildInfo, cte.Item1, bodyExpr);

            // populate all fields
            if (isRecursive)
            {
                cteContext.ConvertToSql(null, 0, ConvertFlags.All);
            }

            return(cteContext);
        }
Ejemplo n.º 3
0
        protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo)
        {
            var mergeContext = (MergeContext)builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0]));

            var kind = MergeKind.Merge;

            if (methodCall.IsSameGenericMethod(MergeWithOutputInto))
            {
                kind = MergeKind.MergeWithOutputInto;
            }
            else if (methodCall.IsSameGenericMethod(MergeWithOutput))
            {
                kind = MergeKind.MergeWithOutput;
            }

            if (kind != MergeKind.Merge)
            {
                var objectType = methodCall.Method.GetGenericArguments()[0];

                var actionField   = SqlField.FakeField(new DbDataType(typeof(string)), "$action", false);
                var insertedTable = SqlTable.Inserted(objectType);
                var deletedTable  = SqlTable.Deleted(objectType);

                mergeContext.Merge.Output = new SqlOutputClause()
                {
                    InsertedTable = insertedTable,
                    DeletedTable  = deletedTable,
                };

                var selectQuery = new SelectQuery();

                var actionFieldContext  = new SingleExpressionContext(null, builder, actionField, selectQuery);
                var deletedTableContext = new TableBuilder.TableContext(builder, selectQuery, deletedTable);
                var insertedTableConext = new TableBuilder.TableContext(builder, selectQuery, insertedTable);

                if (kind == MergeKind.MergeWithOutput)
                {
                    var outputExpression = (LambdaExpression)methodCall.Arguments[1].Unwrap();

                    var outputContext = new MergeOutputContext(
                        buildInfo.Parent,
                        outputExpression,
                        mergeContext,
                        actionFieldContext,
                        deletedTableContext,
                        insertedTableConext
                        );

                    return(outputContext);
                }
                else
                {
                    var outputExpression = (LambdaExpression)methodCall.Arguments[2].Unwrap();

                    var outputTable = methodCall.Arguments[1];
                    var destination = builder.BuildSequence(new BuildInfo(buildInfo, outputTable, new SelectQuery()));

                    UpdateBuilder.BuildSetterWithContext(
                        builder,
                        buildInfo,
                        outputExpression,
                        destination,
                        mergeContext.Merge.Output.OutputItems,
                        actionFieldContext,
                        deletedTableContext,
                        insertedTableConext
                        );

                    mergeContext.Merge.Output.OutputTable = ((TableBuilder.TableContext)destination).SqlTable;
                }
            }

            return(mergeContext);
        }
Ejemplo n.º 4
0
        IBuildContext GetSubQueryContext(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo,
                                         SelectQuery sql,
                                         LambdaExpression innerKeyLambda,
                                         Expression outerKeySelector,
                                         Expression innerKeySelector,
                                         IBuildContext outerKeyContext)
        {
            var subQueryContext = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[1], new SelectQuery()));

            subQueryContext = new SubQueryContext(subQueryContext);

            var subQueryParent     = subQueryContext.Parent;
            var subQueryKeyContext = new ExpressionContext(buildInfo.Parent, subQueryContext, innerKeyLambda);

            // Process SubQuery.
            //
            var subQuerySql = ((SubQueryContext)subQueryContext).SelectQuery;

            // Make join and where for the counter.
            //
            if (outerKeySelector.NodeType == ExpressionType.New)
            {
                var new1 = (NewExpression)outerKeySelector;
                var new2 = (NewExpression)innerKeySelector;

                for (var i = 0; i < new1.Arguments.Count; i++)
                {
                    var arg1 = new1.Arguments[i];
                    var arg2 = new2.Arguments[i];

                    BuildSubQueryJoin(builder, outerKeyContext, arg1, arg2, subQueryKeyContext, subQuerySql);
                }
            }
            else if (outerKeySelector.NodeType == ExpressionType.MemberInit)
            {
                var mi1 = (MemberInitExpression)outerKeySelector;
                var mi2 = (MemberInitExpression)innerKeySelector;

                for (var i = 0; i < mi1.Bindings.Count; i++)
                {
                    if (mi1.Bindings[i].Member != mi2.Bindings[i].Member)
                    {
                        throw new LinqException($"List of member inits does not match for entity type '{outerKeySelector.Type}'.");
                    }

                    var arg1 = ((MemberAssignment)mi1.Bindings[i]).Expression;
                    var arg2 = ((MemberAssignment)mi2.Bindings[i]).Expression;

                    BuildSubQueryJoin(builder, outerKeyContext, arg1, arg2, subQueryKeyContext, subQuerySql);
                }
            }
            else
            {
                BuildSubQueryJoin(builder, outerKeyContext, outerKeySelector, innerKeySelector, subQueryKeyContext, subQuerySql);
            }

            builder.ReplaceParent(subQueryKeyContext, subQueryParent);

            subQuerySql.ParentSelect = sql;
            subQuerySql.Select.Columns.Clear();

            return(subQueryContext);
        }
Ejemplo n.º 5
0
        protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo)
        {
            var sequence = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0]));

            var wrapped = false;

            if (sequence.SelectQuery.Select.HasModifier)
            {
                sequence = new SubQueryContext(sequence);
                wrapped  = true;
            }

            var isContinuousOrder = !sequence.SelectQuery.OrderBy.IsEmpty && methodCall.Method.Name.StartsWith("Then");
            var lambda            = (LambdaExpression)methodCall.Arguments[1].Unwrap();

            SqlInfo[] sql;

            while (true)
            {
                var sparent = sequence.Parent;
                var order   = new ExpressionContext(buildInfo.Parent, sequence, lambda);
                var body    = lambda.Body.Unwrap();
                sql = builder.ConvertExpressions(order, body, ConvertFlags.Key, null);

                builder.ReplaceParent(order, sparent);

                // Do not create subquery for ThenByExtensions
                if (wrapped || isContinuousOrder)
                {
                    break;
                }

                // handle situation when order by uses complex field

                var isComplex = false;

                foreach (var sqlInfo in sql)
                {
                    // immutable expressions will be removed later
                    //
                    var isImmutable = QueryHelper.IsConstant(sqlInfo.Sql);
                    if (isImmutable)
                    {
                        continue;
                    }

                    // possible we have to extend this list
                    //
                    isComplex = null != sqlInfo.Sql.Find(QueryElementType.SqlQuery);
                    if (isComplex)
                    {
                        break;
                    }
                }

                if (!isComplex)
                {
                    break;
                }

                sequence = new SubQueryContext(sequence);
                wrapped  = true;
            }


            if (!isContinuousOrder && !Configuration.Linq.DoNotClearOrderBys)
            {
                sequence.SelectQuery.OrderBy.Items.Clear();
            }

            foreach (var expr in sql)
            {
                // we do not need sorting by immutable values, like "Some", Func("Some"), "Some1" + "Some2". It does nothing for ordering
                //
                if (QueryHelper.IsConstant(expr.Sql))
                {
                    continue;
                }

                sequence.SelectQuery.OrderBy.Expr(expr.Sql, methodCall.Method.Name.EndsWith("Descending"));
            }

            return(sequence);
        }
Ejemplo n.º 6
0
        protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo)
        {
            var sequence = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0]));

            return(new ContainsContext(buildInfo.Parent, methodCall, sequence));
        }
Ejemplo n.º 7
0
        protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo)
        {
            var sequenceExpr             = methodCall.Arguments[0];
            var wrapSequence             = false;
            LambdaExpression?groupingKey = null;
            var groupingKind             = GroupingType.Default;

            if (sequenceExpr.NodeType == ExpressionType.Call)
            {
                var call = (MethodCallExpression)methodCall.Arguments[0];

                if (call.IsQueryable("Select"))
                {
                    var selectParam = (LambdaExpression)call.Arguments[1].Unwrap();
                    var type        = selectParam.Body.Type;

                    if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(ExpressionBuilder.GroupSubQuery <,>))
                    {
                        wrapSequence = true;

                        var selectParamBody = selectParam.Body.Unwrap();
                        MethodCallExpression?groupingMethod = null;
                        if (selectParamBody is MemberInitExpression mi)
                        {
                            var assignment = mi.Bindings.OfType <MemberAssignment>().FirstOrDefault(m => m.Member.Name == "Key");
                            if (assignment?.Expression.NodeType == ExpressionType.Call)
                            {
                                var mc = (MethodCallExpression)assignment.Expression;
                                if (mc.IsSameGenericMethod(Methods.LinqToDB.GroupBy.Rollup, Methods.LinqToDB.GroupBy.Cube, Methods.LinqToDB.GroupBy.GroupingSets))
                                {
                                    groupingMethod = mc;
                                    groupingKey    = (LambdaExpression)mc.Arguments[0].Unwrap();
                                    if (mc.IsSameGenericMethod(Methods.LinqToDB.GroupBy.Rollup))
                                    {
                                        groupingKind = GroupingType.Rollup;
                                    }
                                    else if (mc.IsSameGenericMethod(Methods.LinqToDB.GroupBy.Cube))
                                    {
                                        groupingKind = GroupingType.Cube;
                                    }
                                    else if (mc.IsSameGenericMethod(Methods.LinqToDB.GroupBy.GroupingSets))
                                    {
                                        groupingKind = GroupingType.GroupBySets;
                                    }
                                    else
                                    {
                                        throw new InvalidOperationException();
                                    }
                                }
                            }
                        }

                        if (groupingMethod != null && groupingKey != null)
                        {
                            sequenceExpr = sequenceExpr.Replace(groupingMethod, groupingKey.Body.Unwrap());
                        }
                    }
                }
            }

            var sequence    = builder.BuildSequence(new BuildInfo(buildInfo, sequenceExpr));
            var keySequence = sequence;

            var groupingType    = methodCall.Type.GetGenericArguments()[0];
            var keySelector     = (LambdaExpression)methodCall.Arguments[1].Unwrap();
            var elementSelector = (LambdaExpression)methodCall.Arguments[2].Unwrap();

            if (wrapSequence)
            {
                sequence = new SubQueryContext(sequence);
            }

            sequence = new SubQueryContext(sequence);
            var key = new KeyContext(buildInfo.Parent, keySelector, sequence);

            if (groupingKind != GroupingType.GroupBySets)
            {
                var groupSql = builder.ConvertExpressions(key, keySelector.Body.Unwrap(), ConvertFlags.Key, null);

                foreach (var sql in groupSql.Where(s => s.Sql.ElementType.NotIn(QueryElementType.SqlValue, QueryElementType.SqlParameter)))
                {
                    sequence.SelectQuery.GroupBy.Expr(sql.Sql);
                }
            }
            else
            {
                var goupingSetBody = groupingKey !.Body;
                var groupingSets   = EnumGroupingSets(goupingSetBody).ToArray();
                if (groupingSets.Length == 0)
                {
                    throw new LinqException($"Invalid groping sets expression '{goupingSetBody}'.");
                }

                foreach (var groupingSet in groupingSets)
                {
                    var groupSql = builder.ConvertExpressions(keySequence, groupingSet, ConvertFlags.Key, null);
                    sequence.SelectQuery.GroupBy.Items.Add(
                        new SqlGroupingSet(groupSql.Select(s => keySequence.SelectQuery.Select.AddColumn(s.Sql))));
                }
            }

            sequence.SelectQuery.GroupBy.GroupingType = groupingKind;

            var element = new SelectContext(buildInfo.Parent, elementSelector, sequence /*, key*/);
            var groupBy = new GroupByContext(buildInfo.Parent, sequenceExpr, groupingType, sequence, key, element, builder.IsGroupingGuardDisabled);

            Debug.WriteLine("BuildMethodCall GroupBy:\n" + groupBy.SelectQuery);

            return(groupBy);
        }
Ejemplo n.º 8
0
        protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo)
        {
            var sequence = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0])
            {
                CreateSubQuery = true
            });

            if (sequence.SelectQuery.Select.IsDistinct ||
                sequence.SelectQuery.Select.TakeValue != null ||
                sequence.SelectQuery.Select.SkipValue != null ||
                !sequence.SelectQuery.GroupBy.IsEmpty)
            {
                sequence = new SubQueryContext(sequence);
            }

            if (sequence.SelectQuery.OrderBy.Items.Count > 0)
            {
                if (sequence.SelectQuery.Select.TakeValue == null && sequence.SelectQuery.Select.SkipValue == null)
                {
                    sequence.SelectQuery.OrderBy.Items.Clear();
                }
                else
                {
                    sequence = new SubQueryContext(sequence);
                }
            }

            var context = new AggregationContext(buildInfo.Parent, sequence, methodCall);
            var attr    = GetAggregateDefinition(methodCall, builder.MappingSchema);

            ISqlExpression sqlExpression = null;

            if (attr != null)
            {
                sqlExpression = attr.GetExpression(builder.MappingSchema, sequence.SelectQuery, methodCall, e =>
                {
                    var ex = e.Unwrap();

                    if (ex is LambdaExpression l)
                    {
                        var p   = sequence.Parent;
                        var ctx = new ExpressionContext(buildInfo.Parent, sequence, l);

                        var res = builder.ConvertToSql(ctx, l.Body, true);

                        builder.ReplaceParent(ctx, p);
                        return(res);
                    }
                    return(builder.ConvertToSql(context, ex, true));
                });
            }

            var methodName = methodCall.Method.Name.Replace("Async", "");

            if (sqlExpression == null)
            {
                var sql = sequence.ConvertToSql(null, 0, ConvertFlags.Field).Select(_ => _.Sql).ToArray();

                if (sql.Length == 1 && 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];
                    }
                }

                if (attr != null)
                {
                    sqlExpression = attr.GetExpression(methodCall.Method, sql);
                }
                else
                {
                    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);
        }
Ejemplo n.º 9
0
        protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo)
        {
            var sequence           = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0]));
            var collectionSelector = (LambdaExpression)methodCall.Arguments[1].Unwrap();
            var resultSelector     = (LambdaExpression)methodCall.Arguments[2].Unwrap();

            if (!sequence.SelectQuery.GroupBy.IsEmpty)
            {
                sequence = new SubQueryContext(sequence);
            }

            var context = new SelectManyContext(buildInfo.Parent, collectionSelector, sequence);
            var expr    = collectionSelector.Body.Unwrap();

            var collectionInfo = new BuildInfo(context, expr, new SelectQuery());
            var collection     = builder.BuildSequence(collectionInfo);
            var leftJoin       = collection is DefaultIfEmptyBuilder.DefaultIfEmptyContext;
            var sql            = collection.SelectQuery;

            var sequenceTables = new HashSet <ISqlTableSource>(sequence.SelectQuery.From.Tables[0].GetTables());
            var newQuery       = null != new QueryVisitor().Find(sql, e => e == collectionInfo.SelectQuery);
            var crossApply     = null != new QueryVisitor().Find(sql, e =>
                                                                 e.ElementType == QueryElementType.TableSource && sequenceTables.Contains((ISqlTableSource)e) ||
                                                                 e.ElementType == QueryElementType.SqlField && sequenceTables.Contains(((SqlField)e).Table) ||
                                                                 e.ElementType == QueryElementType.Column && sequenceTables.Contains(((SelectQuery.Column)e).Parent));

            if (collection is JoinBuilder.GroupJoinSubQueryContext)
            {
                var groupJoin = ((JoinBuilder.GroupJoinSubQueryContext)collection).GroupJoin;

                groupJoin.SelectQuery.From.Tables[0].Joins[0].JoinType = SelectQuery.JoinType.Inner;
                groupJoin.SelectQuery.From.Tables[0].Joins[0].IsWeak   = false;
            }

            if (!newQuery)
            {
                context.Collection = new SubQueryContext(collection, sequence.SelectQuery, false);
                return(new SelectContext(buildInfo.Parent, resultSelector, sequence, context));
            }

            if (!crossApply)
            {
                if (!leftJoin)
                {
                    context.Collection = new SubQueryContext(collection, sequence.SelectQuery, true);
                    return(new SelectContext(buildInfo.Parent, resultSelector, sequence, context));
                }
                else
                {
                    var join = SelectQuery.OuterApply(sql);
                    sequence.SelectQuery.From.Tables[0].Joins.Add(join.JoinedTable);
                    context.Collection = new SubQueryContext(collection, sequence.SelectQuery, false);

                    return(new SelectContext(buildInfo.Parent, resultSelector, sequence, context));
                }
            }

            if (collection is TableBuilder.TableContext)
            {
//				if (collectionInfo.IsAssociationBuilt)
//				{
//					context.Collection = new SubQueryContext(collection, sequence.SelectQuery, false);
//					return new SelectContext(buildInfo.Parent, resultSelector, sequence, context);
//				}

                var table = (TableBuilder.TableContext)collection;

                var join = table.SqlTable.TableArguments != null && table.SqlTable.TableArguments.Length > 0 ?
                           (leftJoin ? SelectQuery.OuterApply(sql) : SelectQuery.CrossApply(sql)) :
                           (leftJoin ? SelectQuery.LeftJoin(sql) : SelectQuery.InnerJoin(sql));

                join.JoinedTable.Condition.Conditions.AddRange(sql.Where.SearchCondition.Conditions);
                join.JoinedTable.CanConvertApply = false;

                sql.Where.SearchCondition.Conditions.Clear();

                var collectionParent = collection.Parent as TableBuilder.TableContext;

                // Association.
                //
                if (collectionParent != null && collectionInfo.IsAssociationBuilt)
                {
                    var ts = (SelectQuery.TableSource) new QueryVisitor().Find(sequence.SelectQuery.From, e =>
                    {
                        if (e.ElementType == QueryElementType.TableSource)
                        {
                            var t = (SelectQuery.TableSource)e;
                            return(t.Source == collectionParent.SqlTable);
                        }

                        return(false);
                    });

                    ts.Joins.Add(join.JoinedTable);
                }
                else
                {
                    //if (collectionInfo.IsAssociationBuilt)
                    //{
                    //	collectionInfo.AssosiationContext.ParentAssociationJoin.IsWeak = false;
                    //}
                    //else
                    {
                        sequence.SelectQuery.From.Tables[0].Joins.Add(join.JoinedTable);
                    }
                }

                context.Collection = new SubQueryContext(collection, sequence.SelectQuery, false);
                return(new SelectContext(buildInfo.Parent, resultSelector, sequence, context));
            }
            else
            {
                var join = leftJoin ? SelectQuery.OuterApply(sql) : SelectQuery.CrossApply(sql);
                sequence.SelectQuery.From.Tables[0].Joins.Add(join.JoinedTable);

                context.Collection = new SubQueryContext(collection, sequence.SelectQuery, false);
                return(new SelectContext(buildInfo.Parent, resultSelector, sequence, context));
            }
        }
Ejemplo n.º 10
0
        protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo)
        {
            var sequence = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0]));

            var isSubQuery = sequence.SelectQuery.Select.IsDistinct;

            if (isSubQuery)
            {
                sequence = new SubQueryContext(sequence);
            }

            if (!(sequence.Statement is SqlInsertStatement insertStatement))
            {
                insertStatement    = new SqlInsertStatement(sequence.SelectQuery);
                sequence.Statement = insertStatement;
            }

            switch (methodCall.Arguments.Count)
            {
            case 1:
                // static int Insert<T>              (this IValueInsertable<T> source)
                // static int Insert<TSource,TTarget>(this ISelectInsertable<TSource,TTarget> source)
            {
                sequence.SelectQuery.Select.Columns.Clear();
                foreach (var item in insertStatement.Insert.Items)
                {
                    sequence.SelectQuery.Select.ExprNew(item.Expression);
                }
                break;
            }

            case 2:                      // static int Insert<T>(this Table<T> target, Expression<Func<T>> setter)
            {
                UpdateBuilder.BuildSetter(
                    builder,
                    buildInfo,
                    (LambdaExpression)methodCall.Arguments[1].Unwrap(),
                    sequence,
                    insertStatement.Insert.Items,
                    sequence);

                insertStatement.Insert.Into = ((TableBuilder.TableContext)sequence).SqlTable;
                sequence.SelectQuery.From.Tables.Clear();

                break;
            }

            case 3:                      // static int Insert<TSource,TTarget>(this IQueryable<TSource> source, Table<TTarget> target, Expression<Func<TSource,TTarget>> setter)
            {
                var into = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[1], new SelectQuery()));

                UpdateBuilder.BuildSetter(
                    builder,
                    buildInfo,
                    (LambdaExpression)methodCall.Arguments[2].Unwrap(),
                    into,
                    insertStatement.Insert.Items,
                    sequence);

                sequence.SelectQuery.Select.Columns.Clear();

                foreach (var item in insertStatement.Insert.Items)
                {
                    sequence.SelectQuery.Select.Columns.Add(new SqlColumn(sequence.SelectQuery, item.Expression));
                }

                insertStatement.Insert.Into = ((TableBuilder.TableContext)into).SqlTable;

                break;
            }
            }

            var insert = insertStatement.Insert;

            var q = insert.Into.Fields.Values
                    .Except(insert.Items.Select(e => e.Column))
                    .OfType <SqlField>()
                    .Where(f => f.IsIdentity);

            foreach (var field in q)
            {
                var expr = builder.DataContext.CreateSqlProvider().GetIdentityExpression(insert.Into);

                if (expr != null)
                {
                    insert.Items.Insert(0, new SqlSetExpression(field, expr));

                    if (methodCall.Arguments.Count == 3)
                    {
                        sequence.SelectQuery.Select.Columns.Insert(0, new SqlColumn(sequence.SelectQuery, insert.Items[0].Expression));
                    }
                }
            }

            insertStatement.Insert.WithIdentity = methodCall.Method.Name == "InsertWithIdentity";
            sequence.Statement = insertStatement;

            return(new InsertContext(buildInfo.Parent, sequence, insertStatement.Insert.WithIdentity));
        }
Ejemplo n.º 11
0
            public AssociatedTableContext(
                [JetBrains.Annotations.NotNull] ExpressionBuilder builder,
                [JetBrains.Annotations.NotNull] TableContext parent,
                [JetBrains.Annotations.NotNull] AssociationDescriptor association
                )
                : base(builder, parent.SelectQuery)
            {
                if (builder == null)
                {
                    throw new ArgumentNullException(nameof(builder));
                }
                if (parent == null)
                {
                    throw new ArgumentNullException(nameof(parent));
                }
                if (association == null)
                {
                    throw new ArgumentNullException(nameof(association));
                }

                var type = association.MemberInfo.GetMemberType();
                var left = association.CanBeNull;

                if (typeof(IEnumerable).IsSameOrParentOf(type))
                {
                    var eTypes = type.GetGenericArguments(typeof(IEnumerable <>));
                    type   = eTypes != null && eTypes.Length > 0 ? eTypes[0] : type.GetListItemType();
                    IsList = true;
                }

                OriginalType       = type;
                ObjectType         = GetObjectType();
                EntityDescriptor   = Builder.MappingSchema.GetEntityDescriptor(ObjectType);
                InheritanceMapping = EntityDescriptor.InheritanceMapping;
                SqlTable           = new SqlTable(builder.MappingSchema, ObjectType);

                Association       = association;
                ParentAssociation = parent;

                SqlJoinedTable join;

                var queryMethod = Association.GetQueryMethod(parent.ObjectType, ObjectType);

                if (queryMethod != null)
                {
                    var selectManyMethod = GetAssociationQueryExpression(Expression.Constant(builder.DataContext),
                                                                         queryMethod.Parameters[0], parent.ObjectType, parent.Expression, queryMethod);

                    var ownerTableSource = SelectQuery.From.Tables[0];

                    var buildInfo = new BuildInfo(this, selectManyMethod, new SelectQuery())
                    {
                        IsAssociationBuilt = true
                    };
                    _innerContext = builder.BuildSequence(buildInfo);

                    if (Association.CanBeNull)
                    {
                        _innerContext = new DefaultIfEmptyBuilder.DefaultIfEmptyContext(buildInfo.Parent, _innerContext, null);
                    }

                    var associationQuery = _innerContext.SelectQuery;

                    if (associationQuery.Select.From.Tables.Count < 1)
                    {
                        throw new LinqToDBException("Invalid association query. It is not possible to inline query.");
                    }

                    var foundIndex = associationQuery.Select.From.Tables.FindIndex(t =>
                                                                                   t.Source is SqlTable sqlTable && QueryHelper.IsEqualTables(sqlTable, parent.SqlTable));

                    // try to search table by object type
                    // TODO: review maybe there are another ways to do that
                    if (foundIndex < 0)
                    {
                        foundIndex = associationQuery.Select.From.Tables.FindIndex(t =>
                                                                                   t.Source is SqlTable sqlTable && sqlTable.ObjectType == parent.SqlTable.ObjectType);
                    }

                    if (foundIndex < 0)
                    {
                        throw new LinqToDBException("Invalid association query. It is not possible to inline query. Can not find owner table.");
                    }

                    var sourceToReplace = associationQuery.Select.From.Tables[foundIndex];

                    if (left)
                    {
                        foreach (var joinedTable in sourceToReplace.Joins)
                        {
                            if (joinedTable.JoinType == JoinType.Inner)
                            {
                                joinedTable.JoinType = JoinType.Left;
                            }
                            else if (joinedTable.JoinType == JoinType.CrossApply)
                            {
                                joinedTable.JoinType = JoinType.OuterApply;
                            }

                            joinedTable.IsWeak = true;
                        }
                    }

                    ownerTableSource.Joins.AddRange(sourceToReplace.Joins);

                    // prepare fields mapping to replace fields that will be generated by association query
                    _replaceMap =
                        ((SqlTable)sourceToReplace.Source).Fields.Values.ToDictionary(f => (ISqlExpression)f,
                                                                                      f => parent.SqlTable.Fields[f.Name]);

                    ownerTableSource.Walk(new WalkOptions(), e =>
                    {
                        if (_replaceMap.TryGetValue(e, out var newField))
                        {
                            return(newField);
                        }
                        return(e);
                    });

                    ParentAssociationJoin = sourceToReplace.Joins.FirstOrDefault();
                    join = ParentAssociationJoin;

                    // add rest of tables
                    SelectQuery.From.Tables.AddRange(associationQuery.Select.From.Tables.Where(t => t != sourceToReplace));

                    //TODO: Change AssociatedTableContext base class
                    //SqlTable = null;
                }
                else
                {
                    var psrc = parent.SelectQuery.From[parent.SqlTable];
                    join = left ? SqlTable.WeakLeftJoin().JoinedTable : SqlTable.WeakInnerJoin().JoinedTable;

                    ParentAssociationJoin = join;

                    psrc.Joins.Add(join);

                    for (var i = 0; i < association.ThisKey.Length; i++)
                    {
                        if (!parent.SqlTable.Fields.TryGetValue(association.ThisKey[i], out var field1))
                        {
                            throw new LinqException("Association key '{0}' not found for type '{1}.", association.ThisKey[i], parent.ObjectType);
                        }

                        if (!SqlTable.Fields.TryGetValue(association.OtherKey[i], out var field2))
                        {
                            throw new LinqException("Association key '{0}' not found for type '{1}.", association.OtherKey[i], ObjectType);
                        }

                        //					join.Field(field1).Equal.Field(field2);

                        ISqlPredicate predicate = new SqlPredicate.ExprExpr(
                            field1, SqlPredicate.Operator.Equal, field2);

                        predicate = builder.Convert(parent, predicate);

                        join.Condition.Conditions.Add(new SqlCondition(false, predicate));
                    }

                    if (ObjectType != OriginalType)
                    {
                        var predicate = Builder.MakeIsPredicate(this, OriginalType);

                        if (predicate.GetType() != typeof(SqlPredicate.Expr))
                        {
                            join.Condition.Conditions.Add(new SqlCondition(false, predicate));
                        }
                    }

                    RegularConditionCount = join.Condition.Conditions.Count;
                    ExpressionPredicate   = Association.GetPredicate(parent.ObjectType, ObjectType);

                    if (ExpressionPredicate != null)
                    {
                        ExpressionPredicate = (LambdaExpression)Builder.ConvertExpressionTree(ExpressionPredicate);

                        var expr = Builder.ConvertExpression(ExpressionPredicate.Body.Unwrap());

                        Builder.BuildSearchCondition(
                            new ExpressionContext(parent.Parent, new IBuildContext[] { parent, this }, ExpressionPredicate),
                            expr,
                            join.Condition.Conditions,
                            false);
                    }
                }

                if (!association.AliasName.IsNullOrEmpty() && join != null)
                {
                    join.Table.Alias = association.AliasName;
                }
                else
                {
                    if (!Common.Configuration.Sql.AssociationAlias.IsNullOrEmpty() && join != null)
                    {
                        join.Table.Alias = string.Format(Common.Configuration.Sql.AssociationAlias,
                                                         association.MemberInfo.Name);
                    }
                }

                Init(false);
            }
            protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo)
            {
                var mergeContext = (MergeContext)builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0]));

                var statement = mergeContext.Merge;
                var operation = new SqlMergeOperationClause(MergeOperationType.Insert);

                statement.Operations.Add(operation);

                var predicate = methodCall.Arguments[1];
                var setter    = methodCall.Arguments[2];

                if (!setter.IsNullValue())
                {
                    var setterExpression = (LambdaExpression)setter.Unwrap();
                    mergeContext.AddSourceParameter(setterExpression.Parameters[0]);

                    UpdateBuilder.BuildSetterWithContext(
                        builder,
                        buildInfo,
                        setterExpression,
                        mergeContext.TargetContext,
                        operation.Items,
                        mergeContext.SourceContext);
                }
                else
                {
                    // build setters like QueryRunner.Insert
                    var sqlTable = (SqlTable)statement.Target.Source;
                    var param    = Expression.Parameter(sqlTable.ObjectType, "s");

                    foreach (var field in sqlTable.Fields)
                    {
                        if (field.IsInsertable)
                        {
                            var expression = LinqToDB.Expressions.Extensions.GetMemberGetter(field.ColumnDescriptor.MemberInfo, param);
                            var tgtExpr    = mergeContext.TargetContext.ConvertToSql(builder.ConvertExpression(expression), 1, ConvertFlags.Field)[0].Sql;
                            var srcExpr    = mergeContext.SourceContext.ConvertToSql(builder.ConvertExpression(expression), 1, ConvertFlags.Field)[0].Sql;

                            operation.Items.Add(new SqlSetExpression(tgtExpr, srcExpr));
                        }
                        else if (field.IsIdentity)
                        {
                            var expr = builder.DataContext.CreateSqlProvider().GetIdentityExpression(sqlTable);

                            if (expr != null)
                            {
                                operation.Items.Add(new SqlSetExpression(field, expr));
                            }
                        }
                    }
                }

                if (!predicate.IsNullValue())
                {
                    var condition     = (LambdaExpression)predicate.Unwrap();
                    var conditionExpr = builder.ConvertExpression(condition.Body.Unwrap());

                    operation.Where = new SqlSearchCondition();

                    builder.BuildSearchCondition(
                        new ExpressionContext(null, new[] { mergeContext.SourceContext }, condition),
                        conditionExpr,
                        operation.Where.Conditions);
                }

                return(mergeContext);
            }
Ejemplo n.º 13
0
        protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo)
        {
            var sequence = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0]));

            switch (methodCall.Arguments.Count)
            {
            case 1:                      // int Update<T>(this IUpdateable<T> source)
                CheckAssociation(sequence);
                break;

            case 2:                      // int Update<T>(this IQueryable<T> source, Expression<Func<T,T>> setter)
            {
                CheckAssociation(sequence);

                BuildSetter(
                    builder,
                    buildInfo,
                    (LambdaExpression)methodCall.Arguments[1].Unwrap(),
                    sequence,
                    sequence.SelectQuery.Update.Items,
                    sequence);
                break;
            }

            case 3:
            {
                var expr = methodCall.Arguments[1].Unwrap();

                if (expr is LambdaExpression)
                {
                    CheckAssociation(sequence);

                    // int Update<T>(this IQueryable<T> source, Expression<Func<T,bool>> predicate, Expression<Func<T,T>> setter)
                    //
                    sequence = builder.BuildWhere(buildInfo.Parent, sequence, (LambdaExpression)methodCall.Arguments[1].Unwrap(), false);

                    BuildSetter(
                        builder,
                        buildInfo,
                        (LambdaExpression)methodCall.Arguments[2].Unwrap(),
                        sequence,
                        sequence.SelectQuery.Update.Items,
                        sequence);
                }
                else
                {
                    // static int Update<TSource,TTarget>(this IQueryable<TSource> source, Table<TTarget> target, Expression<Func<TSource,TTarget>> setter)
                    //
                    var into = builder.BuildSequence(new BuildInfo(buildInfo, expr, new SelectQuery()));

                    sequence.ConvertToIndex(null, 0, ConvertFlags.All);
                    new SelectQueryOptimizer(builder.DataContextInfo.SqlProviderFlags, sequence.SelectQuery)
                    .ResolveWeakJoins(new List <ISqlTableSource>());
                    sequence.SelectQuery.Select.Columns.Clear();

                    BuildSetter(
                        builder,
                        buildInfo,
                        (LambdaExpression)methodCall.Arguments[2].Unwrap(),
                        into,
                        sequence.SelectQuery.Update.Items,
                        sequence);

                    var sql = sequence.SelectQuery;

                    sql.Select.Columns.Clear();

                    foreach (var item in sql.Update.Items)
                    {
                        sql.Select.Columns.Add(new SelectQuery.Column(sql, item.Expression));
                    }

                    sql.Update.Table = ((TableBuilder.TableContext)into).SqlTable;
                }

                break;
            }
            }

            sequence.SelectQuery.QueryType = QueryType.Update;

            return(new UpdateContext(buildInfo.Parent, sequence));
        }
Ejemplo n.º 14
0
        protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo)
        {
            var deleteType = methodCall.Method.Name switch
            {
                nameof(LinqExtensions.DeleteWithOutput) => DeleteContext.DeleteType.DeleteOutput,
                nameof(LinqExtensions.DeleteWithOutputInto) => DeleteContext.DeleteType.DeleteOutputInto,
                _ => DeleteContext.DeleteType.Delete,
            };

            var sequence = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0]));

            if (methodCall.Arguments.Count == 2 && deleteType == DeleteContext.DeleteType.Delete)
            {
                sequence = builder.BuildWhere(buildInfo.Parent, sequence, (LambdaExpression)methodCall.Arguments[1].Unwrap(), false);
            }

            var deleteStatement = new SqlDeleteStatement(sequence.SelectQuery);

            sequence.Statement = deleteStatement;

            // Check association.
            //

            if (sequence is SelectContext ctx && ctx.IsScalar)
            {
                var res = ctx.IsExpression(null, 0, RequestFor.Association);

                if (res.Result)
                {
                    var isTableResult = res.Context !.IsExpression(null, 0, RequestFor.Table);
                    if (!isTableResult.Result)
                    {
                        throw new LinqException("Can not retrieve Table context from association.");
                    }

                    var atc = (TableBuilder.TableContext)isTableResult.Context !;
                    deleteStatement.Table = atc.SqlTable;
                }
                else
                {
                    res = ctx.IsExpression(null, 0, RequestFor.Table);

                    if (res.Result && res.Context is TableBuilder.TableContext context)
                    {
                        var tc = context;

                        if (deleteStatement.SelectQuery.From.Tables.Count == 0 || deleteStatement.SelectQuery.From.Tables[0].Source != tc.SelectQuery)
                        {
                            deleteStatement.Table = tc.SqlTable;
                        }
                    }
                }
            }

            var indexedParameters
                = methodCall.Method.GetParameters().Select((p, i) => Tuple.Create(p, i)).ToDictionary(t => t.Item1.Name, t => t.Item2);

            Expression GetArgumentByName(string name)
            {
                return(methodCall.Arguments[indexedParameters[name]]);
            }

            LambdaExpression GetOutputExpression(Type outputType)
            {
                if (!indexedParameters.TryGetValue("outputExpression", out var index))
                {
                    var param = Expression.Parameter(outputType);
                    return(Expression.Lambda(param, param));
                }

                return((LambdaExpression)methodCall.Arguments[index].Unwrap());
            }

            IBuildContext?   outputContext    = null;
            LambdaExpression?outputExpression = null;

            if (deleteType != DeleteContext.DeleteType.Delete)
            {
                outputExpression = GetOutputExpression(methodCall.Method.GetGenericArguments().Last());

                deleteStatement.Output = new SqlOutputClause();

                var deletedTable = SqlTable.Deleted(methodCall.Method.GetGenericArguments()[0]);

                outputContext = new TableBuilder.TableContext(builder, new SelectQuery(), deletedTable);

                deleteStatement.Output.DeletedTable = deletedTable;

                if (deleteType == DeleteContext.DeleteType.DeleteOutputInto)
                {
                    var outputTable = GetArgumentByName("outputTable");
                    var destination = builder.BuildSequence(new BuildInfo(buildInfo, outputTable, new SelectQuery()));

                    UpdateBuilder.BuildSetter(
                        builder,
                        buildInfo,
                        outputExpression,
                        destination,
                        deleteStatement.Output.OutputItems,
                        outputContext);

                    deleteStatement.Output.OutputTable = ((TableBuilder.TableContext)destination).SqlTable;
                }
            }

            if (deleteType == DeleteContext.DeleteType.DeleteOutput)
            {
                return(new DeleteWithOutputContext(buildInfo.Parent, sequence, outputContext !, outputExpression !));
            }

            return(new DeleteContext(buildInfo.Parent, sequence));
        }
Ejemplo n.º 15
0
 protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo)
 {
     return(builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0])));
 }
Ejemplo n.º 16
0
        protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo)
        {
            var sequence = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0])
            {
                CreateSubQuery = true
            });
            var returnType = methodCall.Method.ReturnType;

            if (methodCall.IsAsyncExtension())
            {
                returnType = returnType.GetGenericArguments()[0];
            }

            if (sequence.SelectQuery != buildInfo.SelectQuery)
            {
                if (sequence is JoinBuilder.GroupJoinSubQueryContext)
                {
                    var ctx = new CountContext(buildInfo.Parent, sequence, returnType)
                    {
                        SelectQuery =
                            sequence.SelectQuery
                            //((JoinBuilder.GroupJoinSubQueryContext)sequence).GetCounter(methodCall)
                    };

                    ctx.Sql        = ctx.SelectQuery;
                    ctx.FieldIndex = ctx.SelectQuery.Select.Add(SqlFunction.CreateCount(returnType, ctx.SelectQuery), "cnt");

                    return(ctx);
                }

                if (sequence is GroupByBuilder.GroupByContext)
                {
//					var ctx = new CountContext(buildInfo.Parent, sequence, returnType);
//
//					ctx.Sql        = ctx.SelectQuery;
//					ctx.FieldIndex = ctx.SelectQuery.Select.Add(SqlFunction.CreateCount(returnType, ctx.SelectQuery), "cnt");
//
//					return ctx;

//					return new CountContext(buildInfo.Parent, sequence, returnType)
//					{
//						Sql        = SqlFunction.CreateCount(returnType, sequence.SelectQuery),
//						FieldIndex = -1
//					};
                }
            }

            if (sequence.SelectQuery.Select.IsDistinct ||
                sequence.SelectQuery.Select.TakeValue != null ||
                sequence.SelectQuery.Select.SkipValue != null)
            {
                sequence.ConvertToIndex(null, 0, ConvertFlags.Key);
                sequence = new SubQueryContext(sequence);
            }
            else if (!sequence.SelectQuery.GroupBy.IsEmpty)
            {
                if (!builder.DataContext.SqlProviderFlags.IsSybaseBuggyGroupBy)
                {
                    sequence.SelectQuery.Select.Add(new SqlValue(0));
                }
                else
                {
                    foreach (var item in sequence.SelectQuery.GroupBy.Items)
                    {
                        sequence.SelectQuery.Select.Add(item);
                    }
                }

                sequence = new SubQueryContext(sequence);
            }

            if (sequence.SelectQuery.OrderBy.Items.Count > 0)
            {
                if (sequence.SelectQuery.Select.TakeValue == null && sequence.SelectQuery.Select.SkipValue == null)
                {
                    sequence.SelectQuery.OrderBy.Items.Clear();
                }
                else
                {
                    sequence = new SubQueryContext(sequence);
                }
            }

            var context = new CountContext(buildInfo.Parent, sequence, returnType);

            context.Sql        = context.SelectQuery;
            context.FieldIndex = context.SelectQuery.Select.Add(SqlFunction.CreateCount(returnType, context.SelectQuery), "cnt");

            return(context);
        }
Ejemplo n.º 17
0
        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 == SetOperation.ExceptAll || setOperation == SetOperation.IntersectAll)
                                 ||
                                 !builder.DataContext.SqlProviderFlags.IsDistinctSetOperationsSupported &&
                                 (setOperation == SetOperation.Except || setOperation == SetOperation.Intersect);

            if (needsEmulation)
            {
                // emulation

                var sequence = new SubQueryContext(sequence1);
                var query    = sequence2;
                var except   = query.SelectQuery;

                var sql = sequence.SelectQuery;

                if (setOperation == SetOperation.Except || setOperation == SetOperation.Intersect)
                {
                    sql.Select.IsDistinct = true;
                }

                except.ParentSelect = sql;

                if (setOperation == SetOperation.Except || setOperation == 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));
        }
            public AssociatedTableContext(
                ExpressionBuilder builder,
                TableContext parent,
                AssociationDescriptor association,
                bool forceLeft,
                bool asSubquery
                )
                : base(builder, asSubquery ? new SelectQuery() { ParentSelect = parent.SelectQuery } : parent.SelectQuery)
            {
                if (builder == null)
                {
                    throw new ArgumentNullException(nameof(builder));
                }
                if (parent == null)
                {
                    throw new ArgumentNullException(nameof(parent));
                }
                if (association == null)
                {
                    throw new ArgumentNullException(nameof(association));
                }

                var type = association.MemberInfo.GetMemberType();
                var left = forceLeft || association.CanBeNull;

                if (typeof(IEnumerable).IsSameOrParentOf(type))
                {
                    var eTypes = type.GetGenericArguments(typeof(IEnumerable <>));
                    type   = eTypes != null && eTypes.Length > 0 ? eTypes[0] : type.GetListItemType();
                    IsList = true;
                }

                OriginalType       = type;
                ObjectType         = GetObjectType();
                EntityDescriptor   = Builder.MappingSchema.GetEntityDescriptor(ObjectType);
                InheritanceMapping = EntityDescriptor.InheritanceMapping;
                SqlTable           = new SqlTable(builder.MappingSchema, ObjectType);

                Association       = association;
                ParentAssociation = parent;
                IsSubQuery        = asSubquery;

                if (asSubquery)
                {
                    BuildSubQuery(builder, parent, association, left);
                    Init(false);
                    return;
                }

                SqlJoinedTable join;

                var queryMethod = Association.GetQueryMethod(parent.ObjectType, ObjectType);

                if (queryMethod != null)
                {
                    var selectManyMethod = GetAssociationQueryExpression(Expression.Constant(builder.DataContext),
                                                                         queryMethod.Parameters[0], parent.ObjectType, parent.Expression !, queryMethod);

                    var ownerTableSource = SelectQuery.From.Tables[0];

                    var buildInfo = new BuildInfo(this, selectManyMethod, new SelectQuery())
                    {
                        IsAssociationBuilt = true
                    };
                    _innerContext = builder.BuildSequence(buildInfo);

                    if (Association.CanBeNull)
                    {
                        _innerContext = new DefaultIfEmptyBuilder.DefaultIfEmptyContext(buildInfo.Parent, _innerContext, null);
                    }

                    var associationQuery = _innerContext.SelectQuery;

                    if (associationQuery.Select.From.Tables.Count < 1)
                    {
                        throw new LinqToDBException("Invalid association query. It is not possible to inline query.");
                    }

                    var foundIndex = associationQuery.Select.From.Tables.FindIndex(t =>
                                                                                   t.Source is SqlTable sqlTable && QueryHelper.IsEqualTables(sqlTable, parent.SqlTable));

                    // try to search table by object type
                    // TODO: review maybe there are another way to do that
                    if (foundIndex < 0)
                    {
                        foundIndex = associationQuery.Select.From.Tables.FindIndex(t =>
                                                                                   t.Source is SqlTable sqlTable && sqlTable.ObjectType == parent.SqlTable.ObjectType);
                    }

                    if (foundIndex < 0)
                    {
                        throw new LinqToDBException("Invalid association query. It is not possible to inline query. Can not find owner table.");
                    }

                    var sourceToReplace = associationQuery.Select.From.Tables[foundIndex];

                    if (left)
                    {
                        foreach (var joinedTable in sourceToReplace.Joins)
                        {
                            if (joinedTable.JoinType == JoinType.Inner)
                            {
                                joinedTable.JoinType = JoinType.Left;
                            }
                            else if (joinedTable.JoinType == JoinType.CrossApply)
                            {
                                joinedTable.JoinType = JoinType.OuterApply;
                            }

                            joinedTable.IsWeak = true;
                        }
                    }

                    ownerTableSource.Joins.AddRange(sourceToReplace.Joins);

                    // prepare fields mapping to replace fields that will be generated by association query
                    _replaceMap =
                        ((SqlTable)sourceToReplace.Source).Fields.Values.ToDictionary(f => (ISqlExpression)f,
                                                                                      f => parent.SqlTable.Fields[f.Name]);

                    ownerTableSource.Walk(new WalkOptions(), e =>
                    {
                        if (_replaceMap.TryGetValue(e, out var newField))
                        {
                            return(newField);
                        }
                        return(e);
                    });

                    ParentAssociationJoin = sourceToReplace.Joins.FirstOrDefault();
                    join = ParentAssociationJoin;

                    // add rest of tables
                    SelectQuery.From.Tables.AddRange(associationQuery.Select.From.Tables.Where(t => t != sourceToReplace));

                    //TODO: Change AssociatedTableContext base class
                    //SqlTable = null;
                }
                else
                {
                    var psrc = parent.SelectQuery.From[parent.SqlTable] !;
                    join = left ? SqlTable.WeakLeftJoin().JoinedTable : SqlTable.WeakInnerJoin().JoinedTable;

                    ParentAssociationJoin = join;

                    psrc.Joins.Add(join);
                    BuildAssociationCondition(builder, parent, association, join.Condition);
                }

                SetTableAlias(association, join?.Table);

                Init(false);
            }
Ejemplo n.º 19
0
        protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo)
        {
            var sequence = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0]));

            var isSubQuery = sequence.SelectQuery.Select.IsDistinct;

            if (isSubQuery)
            {
                sequence = new SubQueryContext(sequence);
            }

            if (!(sequence.Statement is SqlInsertStatement insertStatement))
            {
                insertStatement    = new SqlInsertStatement(sequence.SelectQuery);
                sequence.Statement = insertStatement;
            }

            var insertType = InsertContext.InsertType.Insert;

            switch (methodCall.Method.Name)
            {
            case "Insert": insertType = InsertContext.InsertType.Insert;             break;

            case "InsertWithIdentity": insertType = InsertContext.InsertType.InsertWithIdentity; break;

            case "InsertWithOutput": insertType = InsertContext.InsertType.InsertOutput;       break;

            case "InsertWithOutputInto": insertType = InsertContext.InsertType.InsertOutputInto;   break;
            }

            var indexedParameters
                = methodCall.Method.GetParameters().Select((p, i) => Tuple.Create(p, i)).ToDictionary(t => t.Item1.Name, t => t.Item2);

            Expression GetArgumentByName(string name)
            {
                return(methodCall.Arguments[indexedParameters[name]]);
            }

            LambdaExpression GetOutputExpression(Type outputType)
            {
                if (!indexedParameters.TryGetValue("outputExpression", out var index))
                {
                    var param = Expression.Parameter(outputType);
                    return(Expression.Lambda(param, param));
                }

                return((LambdaExpression)methodCall.Arguments[index].Unwrap());
            }

            IBuildContext?   outputContext    = null;
            LambdaExpression?outputExpression = null;

            if (methodCall.Arguments.Count > 0)
            {
                var argument = methodCall.Arguments[0];
                if (typeof(IValueInsertable <>).IsSameOrParentOf(argument.Type) ||
                    typeof(ISelectInsertable <,>).IsSameOrParentOf(argument.Type))
                {
                    // static int Insert<T>              (this IValueInsertable<T> source)
                    // static int Insert<TSource,TTarget>(this ISelectInsertable<TSource,TTarget> source)

                    sequence.SelectQuery.Select.Columns.Clear();
                    foreach (var item in insertStatement.Insert.Items)
                    {
                        sequence.SelectQuery.Select.ExprNew(item.Expression !);
                    }
                }
                else if (methodCall.Arguments.Count > 1 &&
                         typeof(IQueryable <>).IsSameOrParentOf(argument.Type) &&
                         typeof(ITable <>).IsSameOrParentOf(methodCall.Arguments[1].Type))
                {
                    // static int Insert<TSource,TTarget>(this IQueryable<TSource> source, Table<TTarget> target, Expression<Func<TSource,TTarget>> setter)

                    var into   = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[1], new SelectQuery()));
                    var setter = (LambdaExpression)GetArgumentByName("setter").Unwrap();

                    UpdateBuilder.BuildSetter(
                        builder,
                        buildInfo,
                        setter,
                        into,
                        insertStatement.Insert.Items,
                        sequence);

                    sequence.SelectQuery.Select.Columns.Clear();

                    foreach (var item in insertStatement.Insert.Items)
                    {
                        sequence.SelectQuery.Select.Columns.Add(new SqlColumn(sequence.SelectQuery, item.Expression !));
                    }

                    insertStatement.Insert.Into = ((TableBuilder.TableContext)into).SqlTable;
                }
                else if (typeof(ITable <>).IsSameOrParentOf(argument.Type))
                {
                    // static int Insert<T>(this Table<T> target, Expression<Func<T>> setter)
                    // static TTarget InsertWithOutput<TTarget>(this ITable<TTarget> target, Expression<Func<TTarget>> setter)
                    // static TTarget InsertWithOutput<TTarget>(this ITable<TTarget> target, Expression<Func<TTarget>> setter, Expression<Func<TTarget,TOutput>> outputExpression)
                    var argIndex            = 1;
                    var arg                 = methodCall.Arguments[argIndex].Unwrap();
                    LambdaExpression?setter = null;
                    switch (arg)
                    {
                    case LambdaExpression lambda:
                    {
                        setter = lambda;

                        UpdateBuilder.BuildSetter(
                            builder,
                            buildInfo,
                            setter,
                            sequence,
                            insertStatement.Insert.Items,
                            sequence);

                        break;
                    }

                    default:
                    {
                        var objType = arg.Type;

                        var ed   = builder.MappingSchema.GetEntityDescriptor(objType);
                        var into = sequence;
                        var ctx  = new TableBuilder.TableContext(builder, buildInfo, objType);

                        var table = new SqlTable(objType);

                        foreach (var c in ed.Columns.Where(c => !c.SkipOnInsert))
                        {
                            var field = table[c.ColumnName];
                            if (field == null)
                            {
                                continue;
                            }

                            var pe = Expression.MakeMemberAccess(arg, c.MemberInfo);

                            var column    = into.ConvertToSql(pe, 1, ConvertFlags.Field);
                            var parameter = builder.BuildParameterFromArgumentProperty(methodCall, argIndex, field.ColumnDescriptor);

                            insertStatement.Insert.Items.Add(new SqlSetExpression(column[0].Sql, parameter.SqlParameter));
                        }

                        var insertedTable = SqlTable.Inserted(methodCall.Method.GetGenericArguments()[0]);

                        break;
                    }
                    }

                    insertStatement.Insert.Into = ((TableBuilder.TableContext)sequence).SqlTable;
                    sequence.SelectQuery.From.Tables.Clear();
                }

                if (insertType == InsertContext.InsertType.InsertOutput || insertType == InsertContext.InsertType.InsertOutputInto)
                {
                    outputExpression = GetOutputExpression(methodCall.Method.GetGenericArguments().Last());

                    insertStatement.Output = new SqlOutputClause();

                    var insertedTable = SqlTable.Inserted(outputExpression.Parameters[0].Type);

                    outputContext = new TableBuilder.TableContext(builder, new SelectQuery(), insertedTable);

                    insertStatement.Output.InsertedTable = insertedTable;

                    if (insertType == InsertContext.InsertType.InsertOutputInto)
                    {
                        var outputTable = GetArgumentByName("outputTable");
                        var destination = builder.BuildSequence(new BuildInfo(buildInfo, outputTable, new SelectQuery()));

                        UpdateBuilder.BuildSetter(
                            builder,
                            buildInfo,
                            outputExpression,
                            destination,
                            insertStatement.Output.OutputItems,
                            outputContext);

                        insertStatement.Output.OutputTable = ((TableBuilder.TableContext)destination).SqlTable;
                    }
                }
            }

            var insert = insertStatement.Insert;

            var q = insert.Into !.IdentityFields
                    .Except(insert.Items.Select(e => e.Column).OfType <SqlField>());

            foreach (var field in q)
            {
                var expr = builder.DataContext.CreateSqlProvider().GetIdentityExpression(insert.Into);

                if (expr != null)
                {
                    insert.Items.Insert(0, new SqlSetExpression(field, expr));

                    if (methodCall.Arguments.Count == 3)
                    {
                        sequence.SelectQuery.Select.Columns.Insert(0, new SqlColumn(sequence.SelectQuery, insert.Items[0].Expression !));
                    }
                }
            }

            insertStatement.Insert.WithIdentity = insertType == InsertContext.InsertType.InsertWithIdentity;
            sequence.Statement = insertStatement;

            if (insertType == InsertContext.InsertType.InsertOutput)
            {
                return(new InsertWithOutputContext(buildInfo.Parent, sequence, outputContext !, outputExpression !));
            }

            return(new InsertContext(buildInfo.Parent, sequence, insertType, outputExpression));
        }
Ejemplo n.º 20
0
        protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo)
        {
            var sequence = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0]));

            var wrapped = false;

            if (sequence.SelectQuery.Select.TakeValue != null ||
                sequence.SelectQuery.Select.SkipValue != null)
            {
                sequence = new SubQueryContext(sequence);
                wrapped  = true;
            }

            var lambda = (LambdaExpression)methodCall.Arguments[1].Unwrap();

            SqlInfo[] sql;

            while (true)
            {
                var sparent = sequence.Parent;
                var order   = new ExpressionContext(buildInfo.Parent, sequence, lambda);
                var body    = lambda.Body.Unwrap();
                sql = builder.ConvertExpressions(order, body, ConvertFlags.Key);

                builder.ReplaceParent(order, sparent);

                if (wrapped)
                {
                    break;
                }

                // handle situation when order by uses complex field

                var isComplex = false;
                foreach (var sqlInfo in sql)
                {
                    // possible we have to extend this list
                    isComplex = null != new QueryVisitor().Find(sqlInfo.Sql,
                                                                e => e.ElementType == QueryElementType.SqlQuery);
                    if (isComplex)
                    {
                        break;
                    }
                }

                if (!isComplex)
                {
                    break;
                }

                sequence = new SubQueryContext(sequence);
                wrapped  = true;
            }


            if (!methodCall.Method.Name.StartsWith("Then") && !Configuration.Linq.DoNotClearOrderBys)
            {
                sequence.SelectQuery.OrderBy.Items.Clear();
            }

            foreach (var expr in sql)
            {
                var e = builder.ConvertSearchCondition(expr.Sql);
                sequence.SelectQuery.OrderBy.Expr(e, methodCall.Method.Name.EndsWith("Descending"));
            }

            return(sequence);
        }
Ejemplo n.º 21
0
        protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo)
        {
            var sequence = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0]));

            switch (methodCall.Arguments.Count)
            {
            case 1:                      // int Update<T>(this IUpdateable<T> source)
                CheckAssociation(sequence);
                break;

            case 2:                      // int Update<T>(this IQueryable<T> source, Expression<Func<T,T>> setter)
            {
                CheckAssociation(sequence);

                if (sequence.SelectQuery.Select.SkipValue != null || !sequence.SelectQuery.Select.OrderBy.IsEmpty)
                {
                    sequence = new SubQueryContext(sequence);
                }

                BuildSetter(
                    builder,
                    buildInfo,
                    (LambdaExpression)methodCall.Arguments[1].Unwrap(),
                    sequence,
                    sequence.SelectQuery.Update.Items,
                    sequence);
                break;
            }

            case 3:
            {
                var expr = methodCall.Arguments[1].Unwrap();

                if (expr is LambdaExpression && ((LambdaExpression)expr).ReturnType == typeof(bool))
                {
                    CheckAssociation(sequence);

                    // int Update<T>(this IQueryable<T> source, Expression<Func<T,bool>> predicate, Expression<Func<T,T>> setter)
                    //
                    sequence = builder.BuildWhere(buildInfo.Parent, sequence, (LambdaExpression)methodCall.Arguments[1].Unwrap(), false);

                    if (sequence.SelectQuery.Select.SkipValue != null || !sequence.SelectQuery.Select.OrderBy.IsEmpty)
                    {
                        sequence = new SubQueryContext(sequence);
                    }

                    BuildSetter(
                        builder,
                        buildInfo,
                        (LambdaExpression)methodCall.Arguments[2].Unwrap(),
                        sequence,
                        sequence.SelectQuery.Update.Items,
                        sequence);
                }
                else
                {
                    IBuildContext into;

                    if (expr is LambdaExpression)
                    {
                        // static int Update<TSource,TTarget>(this IQueryable<TSource> source, Expression<Func<TSource,TTarget>> target, Expression<Func<TSource,TTarget>> setter)
                        //
                        var body      = ((LambdaExpression)expr).Body;
                        int level     = body.GetLevel();
                        var tableInfo = sequence.IsExpression(body, level, RequestFor.Table);

                        if (tableInfo.Result == false)
                        {
                            throw new LinqException("Expression '{0}' must be a table.");
                        }

                        into = tableInfo.Context;
                    }
                    else
                    {
                        // static int Update<TSource,TTarget>(this IQueryable<TSource> source, Table<TTarget> target, Expression<Func<TSource,TTarget>> setter)
                        //
                        into = builder.BuildSequence(new BuildInfo(buildInfo, expr, new SelectQuery()));
                    }

                    sequence.ConvertToIndex(null, 0, ConvertFlags.All);
                    new SelectQueryOptimizer(builder.DataContext.SqlProviderFlags, sequence.SelectQuery)
                    .ResolveWeakJoins(new List <ISqlTableSource>());
                    sequence.SelectQuery.Select.Columns.Clear();

                    BuildSetter(
                        builder,
                        buildInfo,
                        (LambdaExpression)methodCall.Arguments[2].Unwrap(),
                        into,
                        sequence.SelectQuery.Update.Items,
                        sequence);

                    var sql = sequence.SelectQuery;

                    sql.Select.Columns.Clear();

                    foreach (var item in sql.Update.Items)
                    {
                        sql.Select.Columns.Add(new SqlColumn(sql, item.Expression));
                    }

                    sql.Update.Table = ((TableBuilder.TableContext)into).SqlTable;
                }

                break;
            }
            }

            sequence.SelectQuery.ChangeQueryType(QueryType.Update);

            return(new UpdateContext(buildInfo.Parent, sequence));
        }
Ejemplo n.º 22
0
            protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo)
            {
                var mergeContext = (MergeContext)builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0]));

                var statement = mergeContext.Merge;

                if (methodCall.Arguments.Count == 2)
                {
                    // On<TTarget, TSource>(IMergeableOn<TTarget, TSource> merge, Expression<Func<TTarget, TSource, bool>> matchCondition)
                    var predicate     = methodCall.Arguments[1];
                    var condition     = (LambdaExpression)predicate.Unwrap();
                    var conditionExpr = builder.ConvertExpression(condition.Body.Unwrap());

                    mergeContext.AddTargetParameter(condition.Parameters[0]);
                    mergeContext.AddSourceParameter(condition.Parameters[1]);

                    var filterExpression = BuildSearchCondition(builder, statement, mergeContext.TargetContext, mergeContext.SourceContext,
                                                                condition);

                    statement.On.Conditions.AddRange(filterExpression.Conditions);
                }
                else if (methodCall.Arguments.Count == 3)
                {
                    var targetKeyLambda = ((LambdaExpression)methodCall.Arguments[1].Unwrap());
                    var sourceKeyLambda = ((LambdaExpression)methodCall.Arguments[2].Unwrap());

                    var targetKeySelector = targetKeyLambda.Body.Unwrap();
                    var sourceKeySelector = sourceKeyLambda.Body.Unwrap();

                    var targetKeyContext = new ExpressionContext(buildInfo.Parent, mergeContext.TargetContext, targetKeyLambda);
                    var sourceKeyContext = new ExpressionContext(buildInfo.Parent, mergeContext.SourceContext, sourceKeyLambda);

                    if (targetKeySelector.NodeType == ExpressionType.New)
                    {
                        var new1 = (NewExpression)targetKeySelector;
                        var new2 = (NewExpression)sourceKeySelector;

                        for (var i = 0; i < new1.Arguments.Count; i++)
                        {
                            var arg1 = new1.Arguments[i];
                            var arg2 = new2.Arguments[i];

                            JoinBuilder.BuildJoin(builder, statement.On, targetKeyContext, arg1, sourceKeyContext, arg2);
                        }
                    }
                    else if (targetKeySelector.NodeType == ExpressionType.MemberInit)
                    {
                        // TODO: migrate unordered members support to original code
                        var mi1 = (MemberInitExpression)targetKeySelector;
                        var mi2 = (MemberInitExpression)sourceKeySelector;

                        if (mi1.Bindings.Count != mi2.Bindings.Count)
                        {
                            throw new LinqException($"List of member inits does not match for entity type '{targetKeySelector.Type}'.");
                        }

                        for (var i = 0; i < mi1.Bindings.Count; i++)
                        {
                            var binding2 = (MemberAssignment)mi2.Bindings.Where(b => b.Member == mi1.Bindings[i].Member).FirstOrDefault();
                            if (binding2 == null)
                            {
                                throw new LinqException($"List of member inits does not match for entity type '{targetKeySelector.Type}'.");
                            }

                            var arg1 = ((MemberAssignment)mi1.Bindings[i]).Expression;
                            var arg2 = binding2.Expression;

                            JoinBuilder.BuildJoin(builder, statement.On, targetKeyContext, arg1, sourceKeyContext, arg2);
                        }
                    }
                    else
                    {
                        JoinBuilder.BuildJoin(builder, statement.On, targetKeyContext, targetKeySelector, sourceKeyContext, sourceKeySelector);
                    }
                }
                else
                {
                    // OnTargetKey<TTarget>(IMergeableOn<TTarget, TTarget> merge)
                    var targetType       = statement.Target.SystemType !;
                    var pTarget          = Expression.Parameter(targetType, "t");
                    var pSource          = Expression.Parameter(targetType, "s");
                    var targetDescriptor = builder.MappingSchema.GetEntityDescriptor(targetType);

                    Expression?ex = null;

                    for (var i = 0; i < targetDescriptor.Columns.Count; i++)
                    {
                        var column = targetDescriptor.Columns[i];
                        if (!column.IsPrimaryKey)
                        {
                            continue;
                        }

                        var expr = Expression.Equal(
                            Expression.MakeMemberAccess(pTarget, column.MemberInfo),
                            Expression.MakeMemberAccess(pSource, column.MemberInfo));
                        ex = ex != null?Expression.AndAlso(ex, expr) : expr;
                    }

                    if (ex == null)
                    {
                        throw new LinqToDBException("Method OnTargetKey() needs at least one primary key column");
                    }

                    var condition = Expression.Lambda(ex, pTarget, pSource);

                    var filterExpression = BuildSearchCondition(builder, statement, mergeContext.TargetContext, mergeContext.SourceContext,
                                                                condition);

                    statement.On.Conditions.AddRange(filterExpression.Conditions);
                }

                mergeContext.SourceContext.MatchBuilt();
                return(mergeContext);
            }
Ejemplo n.º 23
0
        protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo)
        {
            var isGroup      = methodCall.Method.Name == "GroupJoin";
            var outerContext = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0], buildInfo.SelectQuery));
            var innerContext = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[1], new SelectQuery()));

            var context = new SubQueryContext(outerContext);

            innerContext = isGroup ? new GroupJoinSubQueryContext(innerContext) : new SubQueryContext(innerContext);

            var join = isGroup ? innerContext.SelectQuery.WeakLeftJoin() : innerContext.SelectQuery.InnerJoin();
            var sql  = context.SelectQuery;

            sql.From.Tables[0].Joins.Add(join.JoinedTable);

            var selector = (LambdaExpression)methodCall.Arguments[4].Unwrap();

            context.SetAlias(selector.Parameters[0].Name);
            innerContext.SetAlias(selector.Parameters[1].Name);

            var outerKeyLambda = ((LambdaExpression)methodCall.Arguments[2].Unwrap());
            var innerKeyLambda = ((LambdaExpression)methodCall.Arguments[3].Unwrap());

            var outerKeySelector = outerKeyLambda.Body.Unwrap();
            var innerKeySelector = innerKeyLambda.Body.Unwrap();

            var outerParent = context.Parent;
            var innerParent = innerContext.Parent;

            var outerKeyContext = new ExpressionContext(buildInfo.Parent, context, outerKeyLambda);
            var innerKeyContext = new InnerKeyContext(buildInfo.Parent, innerContext, innerKeyLambda);

            // Make join and where for the counter.
            //
            if (outerKeySelector.NodeType == ExpressionType.New)
            {
                var new1 = (NewExpression)outerKeySelector;
                var new2 = (NewExpression)innerKeySelector;

                for (var i = 0; i < new1.Arguments.Count; i++)
                {
                    var arg1 = new1.Arguments[i];
                    var arg2 = new2.Arguments[i];

                    BuildJoin(builder, join, outerKeyContext, arg1, innerKeyContext, arg2);
                }
            }
            else if (outerKeySelector.NodeType == ExpressionType.MemberInit)
            {
                var mi1 = (MemberInitExpression)outerKeySelector;
                var mi2 = (MemberInitExpression)innerKeySelector;

                for (var i = 0; i < mi1.Bindings.Count; i++)
                {
                    if (mi1.Bindings[i].Member != mi2.Bindings[i].Member)
                    {
                        throw new LinqException($"List of member inits does not match for entity type '{outerKeySelector.Type}'.");
                    }

                    var arg1 = ((MemberAssignment)mi1.Bindings[i]).Expression;
                    var arg2 = ((MemberAssignment)mi2.Bindings[i]).Expression;

                    BuildJoin(builder, join, outerKeyContext, arg1, innerKeyContext, arg2);
                }
            }
            else
            {
                BuildJoin(builder, join, outerKeyContext, outerKeySelector, innerKeyContext, innerKeySelector);
            }

            builder.ReplaceParent(outerKeyContext, outerParent);
            builder.ReplaceParent(innerKeyContext, innerParent);

            if (isGroup)
            {
                var inner = (GroupJoinSubQueryContext)innerContext;

                inner.Join = join.JoinedTable;
                inner.GetSubQueryContext = () =>
                                           GetSubQueryContext(builder, methodCall, buildInfo, sql,
                                                              innerKeyLambda, outerKeySelector, innerKeySelector, outerKeyContext);

                return(new GroupJoinContext(
                           buildInfo.Parent, selector, context, inner, methodCall.Arguments[1], outerKeyLambda, innerKeyLambda));
            }

            return(new JoinContext(buildInfo.Parent, selector, context, innerContext)
#if DEBUG
            {
                MethodCall = methodCall
            }
#endif
                   );
        }
Ejemplo n.º 24
0
        protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo)
        {
            var sequence = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0]));

            var updateStatement = sequence.Statement as SqlUpdateStatement ?? new SqlUpdateStatement(sequence.SelectQuery);

            sequence.Statement = updateStatement;

            switch (methodCall.Arguments.Count)
            {
            case 1:                     // int Update<T>(this IUpdateable<T> source)
            {
                CheckAssociation(sequence);
                break;
            }

            case 2:                      // int Update<T>(this IQueryable<T> source, Expression<Func<T,T>> setter)
            {
                CheckAssociation(sequence);

                if (sequence.SelectQuery.Select.SkipValue != null || !sequence.SelectQuery.Select.OrderBy.IsEmpty)
                {
                    sequence = new SubQueryContext(sequence);
                    updateStatement.SelectQuery = sequence.SelectQuery;
                    sequence.Statement          = updateStatement;
                }

                BuildSetter(
                    builder,
                    buildInfo,
                    (LambdaExpression)methodCall.Arguments[1].Unwrap(),
                    sequence,
                    updateStatement.Update.Items,
                    sequence);
                break;
            }

            case 3:
            {
                var expr = methodCall.Arguments[1].Unwrap();

                if (expr is LambdaExpression lex && lex.ReturnType == typeof(bool))
                {
                    CheckAssociation(sequence);

                    // int Update<T>(this IQueryable<T> source, Expression<Func<T,bool>> predicate, Expression<Func<T,T>> setter)
                    //
                    sequence = builder.BuildWhere(buildInfo.Parent, sequence, (LambdaExpression)methodCall.Arguments[1].Unwrap(), false);

                    if (sequence.SelectQuery.Select.SkipValue != null || !sequence.SelectQuery.Select.OrderBy.IsEmpty)
                    {
                        sequence = new SubQueryContext(sequence);
                    }

                    updateStatement.SelectQuery = sequence.SelectQuery;
                    sequence.Statement          = updateStatement;

                    BuildSetter(
                        builder,
                        buildInfo,
                        (LambdaExpression)methodCall.Arguments[2].Unwrap(),
                        sequence,
                        updateStatement.Update.Items,
                        sequence);
                }
Ejemplo n.º 25
0
        protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo)
        {
            var outerContext = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0]));
            var innerContext = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[1], new SelectQuery()));

            JoinType joinType;
            var      conditionIndex = 2;

            switch (methodCall.Method.Name)
            {
            case "InnerJoin": joinType = JoinType.Inner; break;

            case "CrossJoin": joinType = JoinType.Inner; conditionIndex = -1; break;

            case "LeftJoin": joinType = JoinType.Left;  break;

            case "RightJoin": joinType = JoinType.Right; break;

            case "FullJoin": joinType = JoinType.Full;  break;

            default:
                conditionIndex = 3;

                joinType = (SqlJoinType)methodCall.Arguments[2].EvaluateExpression() !switch
                {
                    SqlJoinType.Inner => JoinType.Inner,
                    SqlJoinType.Left => JoinType.Left,
                    SqlJoinType.Right => JoinType.Right,
                    SqlJoinType.Full => JoinType.Full,
                    _ => throw new InvalidOperationException($"Unexpected join type: {(SqlJoinType)methodCall.Arguments[2].EvaluateExpression()!}")
                };
                break;
            }

            if (joinType == JoinType.Right || joinType == JoinType.Full)
            {
                outerContext = new DefaultIfEmptyBuilder.DefaultIfEmptyContext(buildInfo.Parent, outerContext, null);
            }
            outerContext = new SubQueryContext(outerContext);


            if (joinType == JoinType.Left || joinType == JoinType.Full)
            {
                innerContext = new DefaultIfEmptyBuilder.DefaultIfEmptyContext(buildInfo.Parent, innerContext, null);
            }
            innerContext = new SubQueryContext(innerContext);

            var selector = (LambdaExpression)methodCall.Arguments[methodCall.Arguments.Count - 1].Unwrap();

            outerContext.SetAlias(selector.Parameters[0].Name);
            innerContext.SetAlias(selector.Parameters[1].Name);

            var joinContext = new JoinContext(buildInfo.Parent, selector, outerContext, innerContext)
#if DEBUG
            {
                MethodCall = methodCall
            }
#endif
            ;

            if (conditionIndex != -1)
            {
                var condition     = (LambdaExpression)methodCall.Arguments[conditionIndex].Unwrap();
                var conditionExpr = condition.GetBody(selector.Parameters[0], selector.Parameters[1]);

                conditionExpr = builder.ConvertExpression(conditionExpr);

                var join = new SqlFromClause.Join(joinType, innerContext.SelectQuery, null, false,
                                                  Array <SqlFromClause.Join> .Empty);

                outerContext.SelectQuery.From.Tables[0].Joins.Add(join.JoinedTable);

                builder.BuildSearchCondition(
                    joinContext,
                    conditionExpr,
                    @join.JoinedTable.Condition.Conditions);
            }
            else
            {
                outerContext.SelectQuery.From.Table(innerContext.SelectQuery);
            }

            return(joinContext);
        }
Ejemplo n.º 26
0
        protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo)
        {
            var sequence = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0]));

            var insertOrUpdateStatement = new SqlInsertOrUpdateStatement(sequence.SelectQuery);

            sequence.Statement = insertOrUpdateStatement;

            UpdateBuilder.BuildSetter(
                builder,
                buildInfo,
                (LambdaExpression)methodCall.Arguments[1].Unwrap(),
                sequence,
                insertOrUpdateStatement.Insert.Items,
                sequence);

            var updateExpr = methodCall.Arguments[2].Unwrap();

            if (!(updateExpr is ConstantExpression constant && constant.Value == null))
            {
                UpdateBuilder.BuildSetter(
                    builder,
                    buildInfo,
                    (LambdaExpression)updateExpr,
                    sequence,
                    insertOrUpdateStatement.Update.Items,
                    sequence);
            }

            insertOrUpdateStatement.Insert.Into  = ((TableBuilder.TableContext)sequence).SqlTable;
            insertOrUpdateStatement.Update.Table = ((TableBuilder.TableContext)sequence).SqlTable;
            insertOrUpdateStatement.SelectQuery.From.Tables.Clear();
            insertOrUpdateStatement.SelectQuery.From.Table(insertOrUpdateStatement.Update.Table);

            if (methodCall.Arguments.Count == 3)
            {
                var table = insertOrUpdateStatement.Insert.Into;
                var keys  = table.GetKeys(false);

                if (keys.Count == 0)
                {
                    throw new LinqException("InsertOrUpdate method requires the '{0}' table to have a primary key.", table.Name);
                }

                var q =
                    (
                        from k in keys
                        join i in insertOrUpdateStatement.Insert.Items on k equals i.Column
                        select new { k, i }
                    ).ToList();

                var missedKey = keys.Except(q.Select(i => i.k)).FirstOrDefault();

                if (missedKey != null)
                {
                    throw new LinqException("InsertOrUpdate method requires the '{0}.{1}' field to be included in the insert setter.",
                                            table.Name,
                                            ((SqlField)missedKey).Name);
                }

                insertOrUpdateStatement.Update.Keys.AddRange(q.Select(i => i.i));
            }
            else
            {
                UpdateBuilder.BuildSetter(
                    builder,
                    buildInfo,
                    (LambdaExpression)methodCall.Arguments[3].Unwrap(),
                    sequence,
                    insertOrUpdateStatement.Update.Keys,
                    sequence);
            }

            return(new InsertOrUpdateContext(buildInfo.Parent, sequence));
        }
Ejemplo n.º 27
0
        public IBuildContext BuildSequence(ExpressionBuilder builder, BuildInfo buildInfo)
        {
            var call = (MethodCallExpression)buildInfo.Expression;

            return(new Context(builder.BuildSequence(new BuildInfo(buildInfo, call.Arguments[0]))));
        }
Ejemplo n.º 28
0
        protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo)
        {
            var outerContext = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0], buildInfo.SelectQuery));
            var innerContext = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[1], new SelectQuery()));

            outerContext = new SubQueryContext(outerContext);
            innerContext = new SubQueryContext(innerContext);

            var selector = (LambdaExpression)methodCall.Arguments[methodCall.Arguments.Count - 1].Unwrap();

            outerContext.SetAlias(selector.Parameters[0].Name);
            innerContext.SetAlias(selector.Parameters[1].Name);

            JoinType joinType;
            var      conditionIndex = 2;

            switch (methodCall.Method.Name)
            {
            case "InnerJoin": joinType = JoinType.Inner; break;

            case "CrossJoin": joinType = JoinType.Inner; conditionIndex = -1; break;

            case "LeftJoin": joinType = JoinType.Left;  break;

            case "RightJoin": joinType = JoinType.Right; break;

            case "FullJoin": joinType = JoinType.Full;  break;

            default:
                conditionIndex = 3;

                var joinValue = (SqlJoinType)methodCall.Arguments[2].EvaluateExpression();

                switch (joinValue)
                {
                case SqlJoinType.Inner: joinType = JoinType.Inner; break;

                case SqlJoinType.Left: joinType = JoinType.Left;  break;

                case SqlJoinType.Right: joinType = JoinType.Right; break;

                case SqlJoinType.Full: joinType = JoinType.Full;  break;

                default: throw new ArgumentOutOfRangeException();
                }

                break;
            }

            if (conditionIndex != -1)
            {
                var condition     = (LambdaExpression)methodCall.Arguments[conditionIndex].Unwrap();
                var conditionExpr = builder.ConvertExpression(condition.Body.Unwrap());

                var join = new SqlFromClause.Join(joinType, innerContext.SelectQuery, null, false,
                                                  Array <SqlFromClause.Join> .Empty);

                outerContext.SelectQuery.From.Tables[0].Joins.Add(join.JoinedTable);

                builder.BuildSearchCondition(
                    new ExpressionContext(null, new[] { outerContext, innerContext }, condition),
                    conditionExpr,
                    join.JoinedTable.Condition.Conditions);
            }
            else
            {
                outerContext.SelectQuery.From.Table(innerContext.SelectQuery);
            }

            return(new SelectContext(buildInfo.Parent, selector, outerContext, innerContext)
#if DEBUG
            {
                MethodCall = methodCall
            }
#endif
                   );
        }