예제 #1
0
        /// <summary>
        /// The get group value factory async.
        /// </summary>
        /// <param name="expressions">
        /// The expressions.
        /// </param>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        private AsyncGroupValueFactory GetGroupValueFactory([NotNull] IEnumerable <AliasedSqlExpression> expressions)
        {
            var rows    = Expression.Parameter(typeof(IAsyncReadOnlyCollection <Row>), "rows");
            var context = Expression.Parameter(typeof(IExecutionContext));

            var lambdaVisitor = new GenericVisitor
            {
                (ExecutionContextExpression e) => context,
                (SourceFieldExpression e) => e.CreateGroupGetter(rows),
                (UnaryExpression e) => e.NodeType == ExpressionType.Convert ? (e.Operand as SourceFieldExpression)?.CreateGroupGetter(rows) : null,
            };

            var fields = expressions.Select(
                expression =>
                Expression.New(
                    QueryPlanBuilder.KeyValuePairConstructor,
                    Expression.Constant(expression.Alias),
                    Expression.Convert(lambdaVisitor.Visit(this.data.ConvertToLinqExpression(expression.Expression)), typeof(object)))).ToArray();

            var rowGetter = Expression.NewArrayInit(typeof(KeyValuePair <string, object>), fields);

            // Expression.Call(TaskExpression.Task(Expression.Call(null, typeof(AsyncEnumerableExtensions))))
            var result =
                Expression.Lambda <AsyncGroupValueFactory>(
                    rowGetter.RewriteTasksToAsyncExpression(),
                    context,
                    rows);

            return(result.Compile());
        }
예제 #2
0
        /// <summary>
        /// Evaluates the <see cref="SourceBase"/>.
        /// </summary>
        /// <param name="expression">
        /// The expression to evaluate.
        /// </param>
        /// <param name="sideEffects">
        /// <c>true</c> if the expression has side effects, <c>false</c> otherwise.
        /// </param>
        /// <returns>
        /// The <see cref="object"/>.
        /// </returns>
        private DataSource Evaluate(SourceBase expression, out bool sideEffects)
        {
            try
            {
                if (this.parsedScript.Context.NodeData.HasSideEffects(expression, variable => this.statements.HasVariableSideEffects(variable)))
                {
                    sideEffects = true;

                    return(null);
                }

                sideEffects = false;

                var linqExpression = GenericVisitor.Visit(
                    (ExecutionContextExpression e) => Expression.Constant(this.statements),
                    this.parsedScript.Context.NodeData.ConvertToDataSource(expression));

                return(Expression.Lambda <Func <DataSource> >(linqExpression).Compile().Invoke());
            }
            catch
            {
                sideEffects = true;

                return(null);
            }
        }
예제 #3
0
        private static LambdaExpression ReplaceEnumerables(IConnectQlFunctions functions, string name, LambdaExpression lambda)
        {
            while (lambda.Parameters.Any(p => p.Type.HasInterface(typeof(IEnumerable <>)) && p.Type != typeof(string)))
            {
                var replacements = lambda.Parameters
                                   .Select(p => new
                {
                    Original  = p,
                    ReplaceBy = p.Type.HasInterface(typeof(IEnumerable <>)) && p.Type != typeof(string) ?
                                Expression.Parameter(typeof(IAsyncEnumerable <>).MakeGenericType(p.Type.GenericTypeArguments[0]), p.Name) :
                                p,
                })
                                   .ToArray();

                var enumerableParameter = replacements.First(p => p.ReplaceBy.Type.HasInterface(typeof(IAsyncEnumerable <>)));

                functions.Logger?.Warning($"Parameter {enumerableParameter.Original.Name} of function {name} is an IEnumerable<T>, this reduces performance, consider using IAsyncEnumerable<T>.");

                lambda = Expression.Lambda(
                    GenericVisitor.Visit(
                        (MethodCallExpression e) =>
                        e.Object != enumerableParameter.Original && !e.Arguments.Contains(enumerableParameter.Original)
                                ? null
                                : TaskExpression.Task(
                            Expression.Call(
                                ConnectQlFunctionsExtensions.ApplyEnumerableFunction.MakeGenericMethod(enumerableParameter.Original.Type.GenericTypeArguments[0], e.Type),
                                enumerableParameter.ReplaceBy,
                                Expression.Lambda(e, enumerableParameter.Original))),
                        lambda.Body),
                    replacements.Select(r => r.ReplaceBy));
            }

            return(lambda);
        }
        public void Visit_AddSquareFunctionality_SquareCalculatedAndSet()
        {
            var     visitor = new GenericVisitor <VisitableBase>();
            Visit_1 square1 = s => s.Property1 *= s.Property1;
            Visit_2 square2 = s => s.Property2 *= s.Property2;

            visitor
            .AddDelegate(square1).AddDelegate(square2);

            var s1 = new Visitable_1 {
                Property1 = 2
            };
            var s2 = new Visitable_2 {
                Property2 = 3
            };

            visitor.Visit(s1);
            Assert.That(s1.Property1, Is.EqualTo(4));

            visitor.Visit(s2);
            Assert.That(s2.Property2, Is.EqualTo(9));
        }
예제 #5
0
        public void DataTypeVisitor_Generic_Simple()
        {
            var v = new GenericVisitor();

            Assert.IsNull(v.Visit(type: null));

            foreach (var t in new[] {
                typeof(int),
                typeof(int[]),
                typeof(Func <int, int>),
            })
            {
                var d = DataType.FromType(t);
                var r = v.Visit(d);
                Assert.AreEqual(t, r, t.ToString());
            }

            var c = new MyDataType();

            Assert.AreSame(c.UnderlyingType, v.Visit(c));

            v.Test();
        }
예제 #6
0
        /// <summary>
        /// Retrieves the data from the source as an <see cref="IAsyncEnumerable{T}"/>.
        /// </summary>
        /// <param name="context">
        /// The context.
        /// </param>
        /// <param name="multiPartQuery">
        /// The query expression.
        /// </param>
        /// <returns>
        /// A task returning the data set.
        /// </returns>
        public IAsyncEnumerable <Row> GetRows(IInternalExecutionContext context, IMultiPartQuery multiPartQuery)
        {
            var expressions = GenericVisitor.Visit((ExecutionContextExpression e) => Expression.Constant(context), this.FilterExpression).SplitByOrExpressions().Distinct(new ExpressionComparer()).ToArray();
            var orderBy     = expressions.Length > 1 ? Enumerable.Empty <OrderByExpression>() : multiPartQuery.OrderByExpressions;
            var result      = expressions.Select(subFilter => this.GetRows(context, this.CreateJoinQuery(context, multiPartQuery.ReplaceOrderBy(orderBy), subFilter)))
                              .Aggregate((current, next) => current.Union(next, new RowIdComparer()));

            if (expressions.Length == 1)
            {
                return(result);
            }

            context.Logger.Verbose($"Expression contains or, splitting in {expressions.Length} parts.");

            return(result.OrderBy(multiPartQuery.OrderByExpressions));
        }
예제 #7
0
        protected internal override Node VisitUseStatement(UseStatement node)
        {
            node = (UseStatement)base.VisitUseStatement(node);

            var context  = Expression.Parameter(typeof(IExecutionContext), "context");
            var getValue = Expression.Lambda <Func <IExecutionContext, object> >(
                GenericVisitor.Visit(
                    (ExecutionContextExpression e) => context,
                    (CompareExpression e) => e.CreateComparer(),
                    this.data.ConvertToLinqExpression(node.SettingFunction)),
                context).Compile();

            this.data.SetQueryPlan(node, new UseDefaultQueryPlan(node.SettingFunction.Name, node.FunctionName, getValue));

            return(node);
        }
            protected internal override Node VisitApplySource(ApplySource node)
            {
                node = (ApplySource)base.VisitApplySource(node);

                Expression factory = Expression.Constant(null, typeof(Func <Row, DataSource>));

                var left     = this.data.GetFactoryExpression(node.Left);
                var right    = this.data.GetFactoryExpression(node.Right);
                var row      = Expression.Parameter(typeof(Row), "row");
                var replaced = GenericVisitor.Visit(
                    (SourceFieldExpression e) => Expression.Call(row, NodeDataProviderDataSourceConverter.GetMethod.MakeGenericMethod(e.Type), Expression.Constant($"{e.SourceName}.{e.FieldName}")),
                    (UnaryExpression e) => e.NodeType == ExpressionType.Convert && e.Operand is SourceFieldExpression
                                               ? Expression.Call(row, NodeDataProviderDataSourceConverter.GetMethod.MakeGenericMethod(e.Type), Expression.Constant($"{((SourceFieldExpression)e.Operand).SourceName}.{((SourceFieldExpression)e.Operand).FieldName}"))
                                               : null,
                    right);

                if (!ReferenceEquals(replaced, right))
                {
                    factory = Expression.Constant(right);
                    right   = GenericVisitor.Visit(
                        (SourceFieldExpression e) => Expression.Default(e.Type),
                        (UnaryExpression e) => e.NodeType == ExpressionType.Convert && e.Operand is SourceFieldExpression
                                                   ? Expression.Default(e.Type)
                                                   : null,
                        right);
                }

                var apply =
                    node.IsOuterApply
                        ? Evaluator.CreateJoin(
                        typeof(OuterApply).GetTypeInfo().DeclaredConstructors.First(),
                        left,
                        right,
                        factory)
                        : Evaluator.CreateJoin(
                        typeof(CrossApply).GetTypeInfo().DeclaredConstructors.First(),
                        left,
                        right,
                        factory);

                this.data.SetFactoryExpression(node, apply);

                return(node);
            }
예제 #9
0
        /// <summary>
        /// The ranges to join filter.
        /// </summary>
        /// <param name="filter">
        /// The filter.
        /// </param>
        /// <returns>
        /// The <see cref="Expression"/>.
        /// </returns>
        private static Expression RangesToJoinFilter(Expression filter)
        {
            return(GenericVisitor.Visit(
                       (CompareExpression node) =>
            {
                var field = node.Left as SourceFieldExpression;
                var range = node.Right as RangeExpression;

                if (field == null || range == null)
                {
                    return null;
                }

                switch (node.CompareType)
                {
                case ExpressionType.Equal:

                    return Expression.AndAlso(
                        CustomExpression.MakeCompare(ExpressionType.GreaterThanOrEqual, field, Expression.Constant(range.Min, range.Type)),
                        CustomExpression.MakeCompare(ExpressionType.LessThanOrEqual, field, Expression.Constant(range.Max, range.Type)));

                case ExpressionType.GreaterThan:
                case ExpressionType.GreaterThanOrEqual:

                    return CustomExpression.MakeCompare(node.CompareType, field, Expression.Constant(range.Min, range.Type));

                case ExpressionType.LessThan:
                case ExpressionType.LessThanOrEqual:

                    return CustomExpression.MakeCompare(node.CompareType, field, Expression.Constant(range.Max, range.Type));

                case ExpressionType.NotEqual:

                    return Expression.Constant(true);

                default:
                    return null;
                }
            },
                       filter));
        }
예제 #10
0
        /// <summary>
        /// Initializes a new instance of the <see cref="ApplyBase"/> class.
        /// </summary>
        /// <param name="left">
        /// The left.
        /// </param>
        /// <param name="right">
        /// The right.
        /// </param>
        /// <param name="rightFactory">
        /// The right Factory.
        /// </param>
        protected ApplyBase([NotNull] DataSource left, [NotNull] DataSource right, [CanBeNull] Expression rightFactory)
            : base(new HashSet <string>(left.Aliases.Concat(right.Aliases)))
        {
            this.left  = left;
            this.right = right;

            this.extraFields = rightFactory?.GetDataSourceFields(this.left).ToArray() ?? new Field[0];

            var context = Expression.Parameter(typeof(IExecutionContext));
            var row     = Expression.Parameter(typeof(Row));

            this.RightFactory = rightFactory == null
                                    ? null
                                    : Expression.Lambda <Func <IExecutionContext, Row, DataSource> >(
                GenericVisitor.Visit(
                    (ExecutionContextExpression e) => context,
                    (SourceFieldExpression f) => f.CreateGetter(row),
                    (UnaryExpression e) => e.NodeType == ExpressionType.Convert ? (e.Operand as SourceFieldExpression)?.CreateGetter(row, e.Type) : null,
                    rightFactory),
                context,
                row).Compile();
        }
예제 #11
0
        /// <summary>
        /// Initializes a new instance of the <see cref="DeclareVariableQueryPlan"/> class.
        /// </summary>
        /// <param name="name">
        /// The name.
        /// </param>
        /// <param name="expression">
        /// The expression.
        /// </param>
        public DeclareVariableQueryPlan(string name, Expression expression)
        {
            var context = Expression.Parameter(typeof(IInternalExecutionContext), "context");

            expression = GenericVisitor.Visit(
                (ExecutionContextExpression e) => context,
                Expression.Call(context, DeclareVariableQueryPlan.SetVariable.MakeGenericMethod(expression.Type), Expression.Constant(name), expression).RewriteTasksToAsyncExpression());

            if (!expression.Type.IsConstructedGenericType || expression.Type.GetGenericTypeDefinition() != typeof(Task <>))
            {
                var setVariable = Expression.Lambda <Action <IInternalExecutionContext> >(expression, context).Compile();

                this.evaluateVariable = ctx =>
                {
                    setVariable(ctx);
                    return(Task.FromResult(true));
                };
            }
            else
            {
                this.evaluateVariable = Expression.Lambda <Func <IExecutionContext, Task> >(expression, context).Compile();
            }
        }
예제 #12
0
        /// <summary>
        /// Gets a factory function for the data source of this.
        /// </summary>
        /// <param name="source">
        /// The <see cref="SourceBase"/> AST node to generate the factory for.
        /// </param>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        private Func <IExecutionContext, Task <DataSource> > GetSourceFactory(SourceBase source)
        {
            var context        = Expression.Parameter(typeof(IExecutionContext), "context");
            var row            = Expression.Parameter(typeof(Row), "row");
            var replaceContext = new GenericVisitor
            {
                (ExecutionContextExpression e) => context,
                (SourceFieldExpression e) => e.CreateGetter(row),
                (GenericVisitor visitor, CompareExpression e) => e.CreateComparer(),
                (UnaryExpression e) => e.NodeType == ExpressionType.Convert && e.Operand is SourceFieldExpression
                                                                    ? ((SourceFieldExpression)e.Operand).CreateGetter(row, e.Type)
                                                                    : null,
            };

            var sourceFactoryLambda = Expression.Lambda(replaceContext.Visit(this.data.ConvertToDataSource(source)), context).RewriteTasksToAsyncExpression();

            if (sourceFactoryLambda.ReturnType != typeof(Task <DataSource>))
            {
                sourceFactoryLambda = Expression.Lambda(Expression.Call(((Func <DataSource, Task <DataSource> >)Task.FromResult).GetMethodInfo(), sourceFactoryLambda.Body), sourceFactoryLambda.Parameters);
            }

            return(((Expression <Func <IExecutionContext, Task <DataSource> > >)sourceFactoryLambda).Compile());
        }
예제 #13
0
        /// <summary>
        /// The ranges to join filter.
        /// </summary>
        /// <param name="filter">
        /// The filter.
        /// </param>
        /// <returns>
        /// The <see cref="Expression"/>.
        /// </returns>
        private static Expression RangesToJoinFilter(Expression filter)
        {
            return(GenericVisitor.Visit(
                       (CompareExpression node) =>
            {
                var rightRange = node.Right as RangeExpression;

                if (rightRange == null)
                {
                    var leftRange = node.Left as RangeExpression;

                    if (leftRange == null)
                    {
                        return null;
                    }

                    switch (node.CompareType)
                    {
                    case ExpressionType.Equal:

                        return Expression.AndAlso(
                            CustomExpression.MakeCompare(ExpressionType.GreaterThanOrEqual, leftRange.MinExpression, node.Right),
                            CustomExpression.MakeCompare(ExpressionType.LessThanOrEqual, leftRange.MaxExpression, node.Right));

                    case ExpressionType.GreaterThan:
                    case ExpressionType.GreaterThanOrEqual:

                        return CustomExpression.MakeCompare(node.CompareType, leftRange.MinExpression, node.Right);

                    case ExpressionType.LessThan:
                    case ExpressionType.LessThanOrEqual:

                        return CustomExpression.MakeCompare(node.CompareType, leftRange.MaxExpression, node.Right);

                    case ExpressionType.NotEqual:

                        return Expression.Constant(true);

                    default:
                        return null;
                    }
                }

                switch (node.CompareType)
                {
                case ExpressionType.Equal:

                    return Expression.AndAlso(
                        CustomExpression.MakeCompare(ExpressionType.GreaterThanOrEqual, node.Left, rightRange.MinExpression),
                        CustomExpression.MakeCompare(ExpressionType.LessThanOrEqual, node.Left, rightRange.MaxExpression));

                case ExpressionType.GreaterThan:
                case ExpressionType.GreaterThanOrEqual:

                    return CustomExpression.MakeCompare(node.CompareType, node.Left, rightRange.MinExpression);

                case ExpressionType.LessThan:
                case ExpressionType.LessThanOrEqual:

                    return CustomExpression.MakeCompare(node.CompareType, node.Left, rightRange.MaxExpression);

                case ExpressionType.NotEqual:

                    return Expression.Constant(true);

                default:
                    return null;
                }
            },
                       filter));
        }
예제 #14
0
 public static Expression GetFilter([NotNull] this IMultiPartQuery query, IExecutionContext context)
 {
     return(query.FilterExpression == null ? null : GenericVisitor.Visit((ExecutionContextExpression e) => Expression.Constant(context), query.FilterExpression));
 }
예제 #15
0
        /// <summary>
        /// Gets a function that creates the values for the selected records from a row.
        /// </summary>
        /// <param name="expressions">
        /// The <see cref="AliasedSqlExpression"/>s to get the values for.
        /// </param>
        /// <param name="fieldList">
        /// A collection that will be filled with all the fields that are used in the select statement.
        /// </param>
        /// <param name="wildCardAliasList">
        /// A collection that will be filled with all the wildcards that are used in the select statement.
        /// </param>
        /// <param name="allSourceAliases">
        /// Contains all source aliases in the query, so when a global wildcard is found, all fields are added.
        /// </param>
        /// <returns>
        /// A delegate.
        /// </returns>
        private Delegate GetValueFactory([NotNull] IEnumerable <AliasedSqlExpression> expressions, ICollection <IField> fieldList, ICollection <string> wildCardAliasList, IEnumerable <string> allSourceAliases)
        {
            var row            = Expression.Parameter(typeof(Row), "row");
            var context        = Expression.Parameter(typeof(IExecutionContext), "context");
            var fieldFactories = new List <LambdaExpression>();

            var lambdaVisitor = new GenericVisitor
            {
                (ExecutionContextExpression e) => context,
                (SourceFieldExpression e) =>
                {
                    fieldList?.Add(new Field(e.SourceName, e.FieldName));

                    return(e.CreateGetter(row));
                },
                (UnaryExpression e) => e.NodeType == ExpressionType.Convert ? (e.Operand as SourceFieldExpression)?.CreateGetter(row, e.Type) : null,
            };

            Func <string, Expression, Expression <Func <IExecutionContext, Row, KeyValuePair <string, object> > > > convertToLambda = (alias, expression) =>
            {
                var lambda = lambdaVisitor.Visit(expression.EvaluateAsValue());
                lambda = lambda.Type == typeof(object) ? lambda : Expression.Convert(lambda, typeof(object));
                return(Expression.Lambda <Func <IExecutionContext, Row, KeyValuePair <string, object> > >(Expression.New(QueryPlanBuilder.KeyValuePairConstructor, Expression.Constant(alias), lambda.CatchErrors()), context, row));
            };

            Expression <ValueFactory> result = null;

            var wildcardIndex  = 0;
            var hasGlobalAlias = false;

            foreach (var expression in expressions)
            {
                if (expression.Expression is WildcardSqlExpression wildcard)
                {
                    if (wildcard.Source != null)
                    {
                        wildCardAliasList.Add(wildcard.Source);
                    }
                    else
                    {
                        hasGlobalAlias = true;
                    }

                    var sourcePrefix = $"{wildcard.Source}.";
                    var index        = wildcardIndex++;
                    var lambda       = string.IsNullOrEmpty(wildcard.Source)
                                     ? (Expression <ValueFactory>)((c, r) => r.ColumnNames.Select(cn => new KeyValuePair <string, object>($"{index}!{cn}", r[cn])))
                                     : (c, r) => r.ColumnNames.Where(cn => cn.StartsWith(sourcePrefix)).Select(cn => new KeyValuePair <string, object>($"{index}!{cn}", r[cn]));

                    lambda = lambda.ReplaceParameter(lambda.Parameters[0], context).ReplaceParameter(lambda.Parameters[1], row);

                    if (fieldFactories.Count != 0)
                    {
                        var fields = QueryPlanBuilder.ToArrayInit <ValueFactory>(fieldFactories);

                        result = result == null ? fields : QueryPlanBuilder.ConcatenateLambdas(result, fields);

                        fieldFactories.Clear();
                    }

                    result = result == null ? lambda : QueryPlanBuilder.ConcatenateLambdas(result, lambda);
                }
                else
                {
                    fieldFactories.Add(convertToLambda(expression.Alias, this.data.ConvertToLinqExpression(expression.Expression)));
                }
            }

            if (fieldFactories.Count != 0)
            {
                var fields = QueryPlanBuilder.ToArrayInit <ValueFactory>(fieldFactories);

                result = result == null ? fields : QueryPlanBuilder.ConcatenateLambdas(result, fields);
            }

            if (hasGlobalAlias)
            {
                foreach (var alias in allSourceAliases)
                {
                    wildCardAliasList.Add(alias);
                }
            }

            var asyncResult = (result ?? ((c, r) => Enumerable.Empty <KeyValuePair <string, object> >())).RewriteTasksToAsyncExpression();

            return(asyncResult.ReturnType == typeof(IEnumerable <KeyValuePair <string, object> >)
                       ? ((Expression <ValueFactory>)asyncResult).Compile()
                       : (Delegate)((Expression <AsyncValueFactory>)asyncResult).Compile());
        }
예제 #16
0
        protected internal override Node VisitInsertStatement(InsertStatement node)
        {
            node = (InsertStatement)base.VisitInsertStatement(node);

            var context        = Expression.Parameter(typeof(IExecutionContext), "context");
            var replaceContext = new GenericVisitor
            {
                (ExecutionContextExpression e) => context,
                (CompareExpression e) => e.CreateComparer(),
            };

            var targetFactory = Expression.Lambda <Func <IExecutionContext, DataTarget> >(replaceContext.Visit(this.data.ConvertToDataTarget(node.Target)), context).Compile();

            this.data.SetQueryPlan(node, new InsertQueryPlan(targetFactory, this.data.GetQueryPlan(node.Select), node.Upsert));

            return(node);
        }
예제 #17
0
 public Expression GetFilter(IExecutionContext context)
 {
     return(this.FilterExpression == null ? null : GenericVisitor.Visit((ExecutionContextExpression e) => Expression.Constant(context), this.FilterExpression));
 }
예제 #18
0
        protected virtual JoinQuery CreateJoinQuery(IExecutionContext context, [NotNull] IMultiPartQuery query, Expression joinFilter)
        {
            var filterParts = (query.FilterExpression == null ? joinFilter : Expression.AndAlso(query.GetFilter(context), joinFilter)).SplitByAndExpressions();

            // Get all parts of the query that contain fields from both sources.
            var joinParts = filterParts.OfType <CompareExpression>()
                            .Where(comparison =>
            {
                var fields = comparison.GetFields().Select(f => f.SourceAlias).ToArray();
                return(fields.Intersect(this.Left.Aliases).Any() && fields.Intersect(this.Right.Aliases).Any());
            })
                            .ToArray();

            // What's left are the filters for only one source. These we can split in a left part, a right part, and a filter part over the result.
            var filter         = filterParts.Except(joinParts, new ExpressionComparer()).DefaultIfEmpty().Aggregate(Expression.AndAlso);
            var leftFilter     = filter.RemoveAllPartsThatAreNotInSource(this.Left);
            var rightFilter    = filter.RemoveAllPartsThatAreNotInSource(this.Right);
            var resultFilter   = filter.Except(leftFilter, rightFilter);
            var joinExpression = joinParts.OrderBy(p => p, new MostSpecificComparer()).First().MoveFieldsToTheLeft(this.Left);
            var ascending      = joinExpression.CompareType != ExpressionType.GreaterThan && joinExpression.CompareType != ExpressionType.GreaterThanOrEqual;

            var leftQuery = new MultiPartQuery
            {
                Fields = query.Fields
                         .Where(f => this.Left.Aliases.Contains(f.SourceAlias))
                         .Concat(joinParts.SelectMany(l => l.GetDataSourceFields(this.Left)))
                         .Concat(resultFilter.GetDataSourceFields(this.Left))
                         .Distinct(),
                FilterExpression   = leftFilter,
                OrderByExpressions = new[]
                {
                    new OrderByExpression(joinExpression.Left, ascending)
                },
            };

            var rightQuery = new MultiPartQuery
            {
                Fields = query.Fields
                         .Where(f => this.Right.Aliases.Contains(f.SourceAlias))
                         .Concat(joinParts.SelectMany(l => l.GetDataSourceFields(this.Right)))
                         .Concat(resultFilter.GetDataSourceFields(this.Right))
                         .Distinct(),
                FilterExpression   = rightFilter,
                OrderByExpressions = new[]
                {
                    new OrderByExpression(joinExpression.Right, ascending)
                },
            };

            var exraJoinFilter = GenericVisitor.Visit(
                (ExecutionContextExpression e) => Expression.Constant(context),
                joinParts.Skip(1).DefaultIfEmpty().Aggregate <Expression>(Expression.AndAlso));

            return(new JoinQuery(
                       leftQuery,
                       rightQuery,
                       joinExpression.Left.GetRowExpression <Row>(),
                       joinExpression.CompareType,
                       joinExpression.Right.GetRowExpression <Row>(),
                       joinParts.DefaultIfEmpty <Expression>().Aggregate(Expression.AndAlso),
                       exraJoinFilter.GetJoinFunction(this.Left),
                       query.OrderByExpressions,
                       resultFilter));
        }