internal ColumnExpression(ProjectionExpression subqueryProjection, TableExpressionBase table, bool nullable)
     : this(subqueryProjection.Alias, table, subqueryProjection.Type, subqueryProjection.Expression.TypeMapping, nullable)
 {
 }
        public void PushdownIntoSubQuery()
        {
            var subquery = Clone("t");

            if (subquery.Limit == null && subquery.Offset == null)
            {
                subquery.ClearOrdering();
            }

            _projectionMapping.Clear();
            var columnNameCounter = 0;
            var index             = 0;
            var result            = new Dictionary <ProjectionMember, Expression>();

            foreach (var projection in subquery._projectionMapping)
            {
                result[projection.Key] = Constant(index);
                if (projection.Value is EntityProjectionExpression entityProjection)
                {
                    var propertyExpressions = new Dictionary <IProperty, ColumnExpression>();
                    foreach (var property in entityProjection.EntityType.GetProperties())
                    {
                        var innerColumn          = entityProjection.GetProperty(property);
                        var projectionExpression = new ProjectionExpression(innerColumn, innerColumn.Name);
                        subquery._projection.Add(projectionExpression);
                        propertyExpressions[property] = new ColumnExpression(projectionExpression, subquery, innerColumn.Nullable);
                        index++;
                    }

                    _projectionMapping[projection.Key] = new EntityProjectionExpression(
                        entityProjection.EntityType, propertyExpressions);
                }
                else
                {
                    var projectionExpression = new ProjectionExpression(
                        (SqlExpression)projection.Value, "c" + columnNameCounter++);
                    subquery._projection.Add(projectionExpression);
                    _projectionMapping[projection.Key] = new ColumnExpression(
                        projectionExpression, subquery, IsNullableProjection(projectionExpression));
                    index++;
                }
            }

            subquery._projectionMapping = result;

            var currentOrderings = _orderings.ToList();

            _orderings.Clear();
            foreach (var ordering in currentOrderings)
            {
                var orderingExpression = ordering.Expression;
                var innerProjection    = subquery._projection.FirstOrDefault(
                    pe => pe.Expression.Equals(orderingExpression));
                if (innerProjection != null)
                {
                    _orderings.Add(new OrderingExpression(new ColumnExpression(innerProjection, subquery, IsNullableProjection(innerProjection)), ordering.Ascending));
                }
                else
                {
                    var projectionExpression = new ProjectionExpression(ordering.Expression, "c" + columnNameCounter++);
                    subquery._projection.Add(projectionExpression);
                    _orderings.Add(new OrderingExpression(
                                       new ColumnExpression(projectionExpression, subquery, IsNullableProjection(projectionExpression)), ordering.Ascending));
                }
            }

            Offset     = null;
            Limit      = null;
            IsDistinct = false;
            Predicate  = null;
            _tables.Clear();
            _tables.Add(subquery);
        }
 private static bool IsNullableProjection(ProjectionExpression projection)
 {
     return(projection.Expression is ColumnExpression column ? column.Nullable : true);
 }
 private static bool IsNullableProjection(ProjectionExpression projectionExpression)
 => projectionExpression.Expression switch
 {