public static HashSet <DbTableAlias> Gather(Expression source)
        {
            var gatherer = new ReferencedAliasGatherer();

            gatherer.Visit(source);
            return(gatherer.aliases);
        }
        private bool CanBeJoinCondition(Expression expression, HashSet <DbTableAlias> left,
                                        HashSet <DbTableAlias> right, HashSet <DbTableAlias> all)
        {
            // an expression is good if it has at least one reference to an alias from both left & right sets and does
            // not have any additional references that are not in both left & right sets
            var referenced = ReferencedAliasGatherer.Gather(expression);
            var leftOkay   = referenced.Intersect(left).Any();
            var rightOkay  = referenced.Intersect(right).Any();
            var subset     = referenced.IsSubsetOf(all);

            return(leftOkay && rightOkay && subset);
        }
        private bool GetEquiJoinKeyExpressions(Expression predicate, DbTableAlias outerAlias,
                                               List <Expression> outerExpressions, List <Expression> innerExpressions)
        {
            if (predicate.NodeType == ExpressionType.Equal)
            {
                var b = (BinaryExpression)predicate;
                DbColumnExpression leftCol  = this.GetColumnExpression(b.Left);
                DbColumnExpression rightCol = this.GetColumnExpression(b.Right);
                if (leftCol != null && rightCol != null)
                {
                    if (leftCol.Alias == outerAlias)
                    {
                        outerExpressions.Add(b.Left);
                        innerExpressions.Add(b.Right);
                        return(true);
                    }
                    else if (rightCol.Alias == outerAlias)
                    {
                        innerExpressions.Add(b.Left);
                        outerExpressions.Add(b.Right);
                        return(true);
                    }
                }
            }

            bool hadKey = false;
            var  parts  = predicate.Split(ExpressionType.And, ExpressionType.AndAlso);

            if (parts.Length > 1)
            {
                foreach (var part in parts)
                {
                    bool hasOuterAliasReference = ReferencedAliasGatherer.Gather(part).Contains(outerAlias);
                    if (hasOuterAliasReference)
                    {
                        if (!GetEquiJoinKeyExpressions(part, outerAlias, outerExpressions, innerExpressions))
                        {
                            return(false);
                        }
                        hadKey = true;
                    }
                }
            }

            return(hadKey);
        }
        protected override Expression VisitJoin(DbJoinExpression join)
        {
            join = (DbJoinExpression)base.VisitJoin(join);

            if (join.Join == DbJoinType.CrossApply || join.Join == DbJoinType.OuterApply)
            {
                if (join.Right is DbTableExpression)
                {
                    return(new DbJoinExpression(DbJoinType.CrossJoin, join.Left, join.Right, null));
                }
                else
                {
                    DbSelectExpression select = join.Right as DbSelectExpression;
                    // Only consider rewriting cross apply if
                    //   1) right side is a select
                    //   2) other than in the where clause in the right-side select, no left-side declared aliases are referenced
                    //   3) and has no behavior that would change semantics if the where clause is removed (like groups, aggregates, take, skip, etc).
                    // Note: it is best to attempt this after redundant subqueries have been removed.
                    if (select != null &&
                        select.Take == null &&
                        select.Skip == null &&
                        !AggregateChecker.HasAggregates(select) &&
                        (select.GroupBy == null || select.GroupBy.Count == 0))
                    {
                        DbSelectExpression     selectWithoutWhere = select.SetWhere(null);
                        HashSet <DbTableAlias> referencedAliases  = ReferencedAliasGatherer.Gather(selectWithoutWhere);
                        HashSet <DbTableAlias> declaredAliases    = DeclaredAliasGatherer.Gather(join.Left);
                        referencedAliases.IntersectWith(declaredAliases);
                        if (referencedAliases.Count == 0)
                        {
                            Expression where = select.Where;
                            select           = selectWithoutWhere;
                            var pc = ColumnProjector.ProjectColumns(this.CanBeColumn, where, select.Columns, select.Alias, DeclaredAliasGatherer.Gather(select.From));
                            select = select.SetColumns(pc.Columns);
                            where  = pc.Projector;
                            DbJoinType jt = (where == null) ? DbJoinType.CrossJoin :
                                            (join.Join == DbJoinType.CrossApply ? DbJoinType.InnerJoin : DbJoinType.LeftOuter);
                            return(new DbJoinExpression(jt, join.Left, select, where));
                        }
                    }
                }
            }

            return(join);
        }
 public static HashSet<DbTableAlias> Gather(Expression source)
 {
     var gatherer = new ReferencedAliasGatherer();
     gatherer.Visit(source);
     return gatherer.aliases;
 }