private InputFragment WrapJoinInputIfNecessary(InputFragment fragment, bool isRightPart)
 {
     if (fragment is SelectStatement || fragment is UnionFragment)
     {
         fragment.Wrap(scope);
         fragment.Scoped = true;
     }
     else if (fragment is JoinFragment && isRightPart)
     {
         SelectStatement select = new SelectStatement(this);
         select.From = fragment;
         select.Name = fragment.Name;
         select.Wrap(scope);
         return(select);
     }
     return(fragment);
 }
        public override SqlFragment Visit(DbApplyExpression expression)
        {
            DbExpressionBinding inputBinding = expression.Input;
            InputFragment       input        = VisitInputExpression(inputBinding.Expression, inputBinding.VariableName, inputBinding.VariableType);
            DbExpressionBinding applyBinding = expression.Apply;
            InputFragment       apply        = VisitInputExpression(applyBinding.Expression, applyBinding.VariableName, applyBinding.VariableType);
            SelectStatement     select       = new SelectStatement(this);
            bool isInputSelect = false;

            if (!(input is TableFragment))
            {
                input.Wrap(scope);
                isInputSelect = true;
            }
            apply.Wrap(scope);
            select.From = input;
            select.Wrap(scope);
            if (apply is SelectStatement)
            {
                SelectStatement applySel = apply as SelectStatement;
                foreach (ColumnFragment f in applySel.Columns)
                {
                    SelectStatement newColSelect = new SelectStatement(this);
                    newColSelect.From  = applySel.From;
                    newColSelect.Where = applySel.Where;
                    if (isInputSelect)
                    {
                        VisitAndReplaceTableName(newColSelect.Where, (input as SelectStatement).From.Name, input.Name, null);
                    }
                    newColSelect.Limit      = applySel.Limit;
                    newColSelect.OrderBy    = applySel.OrderBy;
                    newColSelect.Skip       = applySel.Skip;
                    newColSelect.GroupBy    = applySel.GroupBy;
                    newColSelect.IsDistinct = applySel.IsDistinct;
                    newColSelect.Columns.Add(f);

                    newColSelect.Wrap(scope);
                    scope.Add(applySel.From.Name, applySel.From);

                    ColumnFragment newCol = new ColumnFragment(apply.Name, f.ColumnName);
                    newCol.Literal = newColSelect;
                    newCol.PushInput(newCol.ColumnName);
                    newCol.PushInput(apply.Name);
                    select.AddColumn(newCol, scope);
                    if (string.IsNullOrEmpty(newCol.ColumnAlias))
                    {
                        newColSelect.Name  = newCol.ColumnName;
                        newCol.ColumnAlias = newCol.ColumnName;
                    }
                    scope.Remove(newColSelect);
                }
                scope.Remove(applySel.From);
                scope.Remove(apply);
            }
            else if (apply is UnionFragment)
            {
                UnionFragment uf = apply as UnionFragment;
                if (uf.Left == null || uf.Right == null)
                {
                    throw new Exception("Union fragment is not properly formed.");
                }

                var left  = uf.Left as SelectStatement;
                var right = uf.Right as SelectStatement;

                if (left == null || right == null)
                {
                    throw new NotImplementedException();
                }

                var whereleft  = left.Where as BinaryFragment;
                var whereright = right.Where as BinaryFragment;

                if (whereleft == null || whereright == null)
                {
                    throw new NotImplementedException();
                }

                LiteralFragment literalFragmentWhere = null;

                //checking where left
                if (whereleft.Left is ColumnFragment && whereleft.Right is ColumnFragment)
                {
                    // we replace the where part for a dummy one
                    if (whereright.Left is ColumnFragment && whereright.Right is ColumnFragment)
                    {
                        literalFragmentWhere = new LiteralFragment("1 = 1");
                    }
                }

                if (literalFragmentWhere == null)
                {
                    throw new NotImplementedException();
                }

                var leftouterjoin = new JoinFragment();
                leftouterjoin.JoinType = Metadata.GetOperator(DbExpressionKind.LeftOuterJoin);
                leftouterjoin.Name     = apply.Name;

                // validating that column fragment on the left matches the name
                // for the input fragment
                var leftColumnFragment = whereleft.Left as ColumnFragment;

                if (leftColumnFragment == null)
                {
                    throw new NotImplementedException();
                }

                if (!leftColumnFragment.TableName.Equals(input.Name))
                {
                    new NotImplementedException();
                }

                var conditionJoin = new BinaryFragment();
                conditionJoin.Left = whereleft.Left;

                //update to the new reference
                var newColumnFragment = whereright.Right as ColumnFragment;
                if (newColumnFragment != null)
                {
                    newColumnFragment.TableName = uf.Name;
                }
                conditionJoin.Right    = newColumnFragment;
                conditionJoin.Operator = whereleft.Operator;

                leftouterjoin.Condition = conditionJoin;

                (uf.Left as SelectStatement).Where  = literalFragmentWhere;
                (uf.Right as SelectStatement).Where = literalFragmentWhere;

                leftouterjoin.Left  = input;
                leftouterjoin.Right = uf;

                return(leftouterjoin);
            }
            return(select);
        }