private SqlSelect ProcessApplyViaSubqueries(ApplyProvider provider, SqlProvider left, SqlProvider right, bool shouldUseQueryReference)
        {
            var       rightQuery = right.Request.Statement;
            SqlSelect query;

            if (shouldUseQueryReference)
            {
                var leftTable = left.PermanentReference;
                query = SqlDml.Select(leftTable);
                query.Columns.AddRange(leftTable.Columns.Cast <SqlColumn>());
            }
            else
            {
                query = left.Request.Statement.ShallowClone();
            }

            var isApplyExistence =
                provider.Right.Type == ProviderType.Existence ||
                provider.Right.Type == ProviderType.Select && provider.Right.Sources[0].Type == ProviderType.Existence;

            if (isApplyExistence)
            {
                for (int i = 0; i < rightQuery.Columns.Count; i++)
                {
                    var column = rightQuery.Columns[i];
                    if (provider.IsInlined)
                    {
                        var columnStub = SqlDml.ColumnStub(column);
                        var userColumn = ExtractUserColumn(column);
                        stubColumnMap.Add(columnStub, userColumn.Expression);
                        column = columnStub;
                    }
                    query.Columns.Add(column);
                }
            }
            else
            {
                if (provider.IsInlined)
                {
                    for (int i = 0; i < rightQuery.Columns.Count; i++)
                    {
                        var subquery  = rightQuery.ShallowClone();
                        var sqlColumn = subquery.Columns[i];
                        if (IsColumnStub(sqlColumn))
                        {
                            var columnStub = ExtractColumnStub(sqlColumn);
                            subquery.Columns.Clear();
                            subquery.Columns.Add(columnStub.Column);
                            query.Columns.Add(subquery, sqlColumn.Name);
                        }
                        else
                        {
                            var columnRef = (SqlColumnRef)sqlColumn;
                            var column    = columnRef.SqlColumn;
                            subquery.Columns.Clear();
                            subquery.Columns.Add(column);
                            var columnName    = ProcessAliasedName(provider.Right.Header.Columns[i].Name);
                            var userColumnRef = SqlDml.ColumnRef(SqlDml.Column(subquery), columnName);
                            var columnStub    = SqlDml.ColumnStub(userColumnRef);
                            stubColumnMap.Add(columnStub, subquery);
                            query.Columns.Add(columnStub);
                        }
                    }
                }
                else
                {
                    for (int i = 0; i < rightQuery.Columns.Count; i++)
                    {
                        var subquery = rightQuery.ShallowClone();
                        var column   = subquery.Columns[i];
                        if (IsColumnStub(column))
                        {
                            var columnStub = ExtractColumnStub(column);
                            subquery.Columns.Clear();
                            subquery.Columns.Add(columnStub.Column);
                            query.Columns.Add(subquery, column.Name);
                        }
                        else
                        {
                            var columnRef = (SqlColumnRef)column;
                            var sqlColumn = columnRef.SqlColumn;
                            subquery.Columns.Clear();
                            subquery.Columns.Add(sqlColumn);
                            query.Columns.Add(subquery, columnRef.Name);
                        }
                    }
                }
            }
            return(query);
        }
Exemplo n.º 2
0
        private static bool ShouldUseQueryReference(CompilableProvider origin, SqlProvider compiledSource)
        {
            var sourceSelect = compiledSource.Request.Statement;

            if (sourceSelect.From == null)
            {
                return(false);
            }

            var calculatedColumnIndexes = sourceSelect.Columns
                                          .Select((c, i) => IsCalculatedColumn(c) ? i : -1)
                                          .Where(i => i >= 0)
                                          .ToList();
            var containsCalculatedColumns = calculatedColumnIndexes.Count > 0;
            var rowNumberIsUsed           = calculatedColumnIndexes.Count > 0 && sourceSelect.Columns
                                            .Select((c, i) => new { c, i })
                                            .Any(a => calculatedColumnIndexes.Contains(a.i) && ExtractUserColumn(a.c).Expression is SqlRowNumber);
            var pagingIsUsed   = !sourceSelect.Limit.IsNullReference() || !sourceSelect.Offset.IsNullReference() || rowNumberIsUsed;
            var groupByIsUsed  = sourceSelect.GroupBy.Count > 0;
            var distinctIsUsed = sourceSelect.Distinct;
            var filterIsUsed   = !sourceSelect.Where.IsNullReference();

            if (origin.Type == ProviderType.Filter)
            {
                var filterProvider    = (FilterProvider)origin;
                var usedColumnIndexes = new TupleAccessGatherer().Gather(filterProvider.Predicate.Body);
                return(pagingIsUsed || usedColumnIndexes.Any(calculatedColumnIndexes.Contains));
            }

            if (origin.Type == ProviderType.Select)
            {
                return(distinctIsUsed);
            }

            if (origin.Type == ProviderType.RowNumber)
            {
                var usedColumnIndexes = origin.Header.Order.Select(o => o.Key);
                return(pagingIsUsed || groupByIsUsed || distinctIsUsed || usedColumnIndexes.Any(calculatedColumnIndexes.Contains));
            }

            if (origin.Type == ProviderType.Calculate)
            {
                var calculateProvider = (CalculateProvider)origin;
                var columnGatherer    = new TupleAccessGatherer();
                var usedColumnIndexes = new List <int>();
                foreach (var column in calculateProvider.CalculatedColumns)
                {
                    usedColumnIndexes.AddRange(
                        columnGatherer.Gather(column.Expression.Body, column.Expression.Parameters[0]));
                }

                return(usedColumnIndexes.Any(calculatedColumnIndexes.Contains));
            }

            if (origin.Type == ProviderType.Aggregate)
            {
                var aggregateProvider = (AggregateProvider)origin;
                var columnGatherer    = new TupleAccessGatherer();
                var usedColumnIndexes = (aggregateProvider.AggregateColumns ?? Enumerable.Empty <AggregateColumn>())
                                        .Select(ac => ac.SourceIndex)
                                        .Concat(aggregateProvider.GroupColumnIndexes)
                                        .ToList();

                return(usedColumnIndexes.Any(calculatedColumnIndexes.Contains) || pagingIsUsed || distinctIsUsed || groupByIsUsed);
            }

            if (origin.Type.In(ProviderType.Take, ProviderType.Skip, ProviderType.Paging))
            {
                var sortProvider = origin.Sources[0] as SortProvider;
                var orderingOverCalculatedColumn = sortProvider != null &&
                                                   sortProvider.Header.Order
                                                   .Select(order => order.Key)
                                                   .Any(calculatedColumnIndexes.Contains);
                return(distinctIsUsed || pagingIsUsed || groupByIsUsed || orderingOverCalculatedColumn);
            }

            if (origin.Type == ProviderType.Apply)
            {
                return(containsCalculatedColumns || distinctIsUsed || pagingIsUsed || groupByIsUsed);
            }

            if (origin.Type == ProviderType.Join)
            {
                var shouldUseQueryReference = distinctIsUsed || pagingIsUsed || groupByIsUsed;
                if (shouldUseQueryReference)
                {
                    return(true);
                }
                var joinProvider = (JoinProvider)origin;
                var isRight      = joinProvider.Right == compiledSource.Origin;
                var indexes      = joinProvider.EqualIndexes.Select(p => isRight ? p.Second : p.First);
                return((joinProvider.JoinType == JoinType.LeftOuter && filterIsUsed && isRight) ||
                       (containsCalculatedColumns && indexes.Any(calculatedColumnIndexes.Contains)));
            }

            if (origin.Type == ProviderType.PredicateJoin)
            {
                var shouldUseQueryReference = distinctIsUsed || pagingIsUsed || groupByIsUsed;
                if (shouldUseQueryReference)
                {
                    return(true);
                }
                var joinProvider = (PredicateJoinProvider)origin;
                var isRight      = joinProvider.Right == compiledSource.Origin;
                var indexes      = new TupleAccessGatherer().Gather(joinProvider.Predicate.Body, joinProvider.Predicate.Parameters[isRight ? 1 : 0]);
                return((joinProvider.JoinType == JoinType.LeftOuter && filterIsUsed && isRight) ||
                       (containsCalculatedColumns && indexes.Any(calculatedColumnIndexes.Contains)));
            }

            if (origin.Type == ProviderType.Sort)
            {
                if (distinctIsUsed)
                {
                    return(true);
                }
                var orderingOverCalculatedColumn = origin.Header.Order
                                                   .Select(order => order.Key)
                                                   .Any(calculatedColumnIndexes.Contains);
                return(orderingOverCalculatedColumn);
            }

            return(containsCalculatedColumns || distinctIsUsed || pagingIsUsed || groupByIsUsed);
        }
        private SqlSelect ProcessApplyViaCrossApply(ApplyProvider provider, SqlProvider left, SqlProvider right)
        {
            var leftShouldUseReference = ShouldUseQueryReference(provider, left);
            var leftTable = leftShouldUseReference
        ? left.PermanentReference
        : left.Request.Statement.From;
            var leftColumns = leftShouldUseReference
        ? leftTable.Columns.Cast <SqlColumn>()
        : left.Request.Statement.Columns;

            var rightShouldUseReference = ShouldUseQueryReference(provider, right);
            var rightTable = rightShouldUseReference
        ? right.PermanentReference
        : right.Request.Statement.From;
            var rightColumns = rightShouldUseReference
        ? rightTable.Columns.Cast <SqlColumn>()
        : right.Request.Statement.Columns;

            var joinType = provider.ApplyType == JoinType.LeftOuter
        ? SqlJoinType.LeftOuterApply
        : SqlJoinType.CrossApply;

            var joinedTable = SqlDml.Join(
                joinType,
                leftTable,
                rightTable,
                leftColumns.ToList(),
                rightColumns.ToList());

            var query = SqlDml.Select(joinedTable);

            if (!leftShouldUseReference)
            {
                query.Where &= left.Request.Statement.Where;
            }
            if (!rightShouldUseReference)
            {
                query.Where &= right.Request.Statement.Where;
            }
            query.Columns.AddRange(joinedTable.AliasedColumns);
            return(query);
        }