protected override Expression VisitProjection(ProjectionExpression proj)
 {
     proj = (ProjectionExpression)base.VisitProjection(proj);
     if (proj.Source is SelectExpression select && select.From is QueryExpression)
     {
         List <SelectExpression> redundant = RedundantSubqueryGatherer.Gather(proj.Source);
         if (redundant != null)
         {
             proj = SubqueryRemover.Remove(proj, redundant);
         }
     }
     return(proj);
 }
        protected override Expression VisitSelect(SelectExpression select)
        {
            select = (SelectExpression)base.VisitSelect(select);

            // first remove all purely redundant subqueries
            List <SelectExpression> redundant = RedundantSubqueryGatherer.Gather(select.From);

            if (redundant != null)
            {
                select = SubqueryRemover.Remove(select, redundant);
            }

            return(select);
        }
            protected override Expression VisitSelect(SelectExpression select)
            {
                bool wasTopLevel = _isTopLevel;

                _isTopLevel = false;

                select = (SelectExpression)base.VisitSelect(select);

                // next attempt to merge subqueries that would have been removed by the above
                // logic except for the existence of a where clause
                while (CanMergeWithFrom(select, wasTopLevel))
                {
                    SelectExpression fromSelect = GetLeftMostSelect(select.From);

                    // remove the redundant subquery
                    select = SubqueryRemover.Remove(select, fromSelect);

                    // merge where expressions
                    Expression where;
                    Expression having;
                    if (fromSelect.GroupBy != null && fromSelect.GroupBy.Count > 0)
                    {
                        where  = fromSelect.Where;
                        having = select.Where;
                        if (fromSelect.Having != null)
                        {
                            having = having != null?Expression.AndAlso(fromSelect.Having, having) : fromSelect.Having;
                        }
                    }
                    else
                    {
                        where = select.Where;
                        if (fromSelect.Where != null)
                        {
                            where = where != null?Expression.AndAlso(fromSelect.Where, where) : fromSelect.Where;
                        }
                        having = null;
                    }

                    var        orderBy = select.OrderBy != null && select.OrderBy.Count > 0 ? select.OrderBy : fromSelect.OrderBy;
                    var        groupBy = select.GroupBy != null && select.GroupBy.Count > 0 ? select.GroupBy : fromSelect.GroupBy;
                    Expression offset  = select.Offset;
                    if (fromSelect.Offset != null)
                    {
                        offset = offset == null ? fromSelect.Offset : Expression.Add(fromSelect.Offset, offset);
                    }
                    Expression limit = select.Limit ?? fromSelect.Limit;
                    if (fromSelect.Limit != null)
                    {
                        limit = limit == null ? fromSelect.Limit :
                                Expression.Call(typeof(Math), nameof(Math.Min), null, fromSelect.Limit, limit);
                    }
                    bool isDistinct = select.IsDistinct | fromSelect.IsDistinct;

                    if (where != select.Where ||
                        orderBy != select.OrderBy ||
                        groupBy != select.GroupBy ||
                        having != select.Having ||
                        isDistinct != select.IsDistinct ||
                        offset != select.Offset ||
                        limit != select.Limit)
                    {
                        select = new SelectExpression(
                            select.Alias, select.Columns, select.From, where, groupBy, having, orderBy, offset, limit, isDistinct
                            );
                    }
                }

                return(select);
            }