Exemplo n.º 1
0
        private SqlExpression VisitJoinPredicate(SqlBinaryExpression predicate)
        {
            var canOptimize = _canOptimize;

            _canOptimize = true;

            if (predicate.OperatorType == ExpressionType.Equal)
            {
                var newLeft  = (SqlExpression)Visit(predicate.Left);
                var newRight = (SqlExpression)Visit(predicate.Right);
                _canOptimize = canOptimize;

                return(predicate.Update(newLeft, newRight));
            }

            if (predicate.OperatorType == ExpressionType.AndAlso)
            {
                var newPredicate = (SqlExpression)VisitSqlBinary(predicate);
                _canOptimize = canOptimize;

                return(newPredicate);
            }

            throw new InvalidOperationException("Unexpected join predicate shape: " + predicate);
        }
Exemplo n.º 2
0
        protected override Expression VisitSqlBinary(SqlBinaryExpression sqlBinaryExpression)
        {
            var parentIsSearchCondition = _isSearchCondition;

            switch (sqlBinaryExpression.OperatorType)
            {
            // Only logical operations need conditions on both sides
            case ExpressionType.AndAlso:
            case ExpressionType.OrElse:
                _isSearchCondition = true;
                break;

            default:
                _isSearchCondition = false;
                break;
            }

            var newLeft  = (SqlExpression)Visit(sqlBinaryExpression.Left);
            var newRight = (SqlExpression)Visit(sqlBinaryExpression.Right);

            _isSearchCondition = parentIsSearchCondition;

            sqlBinaryExpression = sqlBinaryExpression.Update(newLeft, newRight);
            var condition = sqlBinaryExpression.OperatorType == ExpressionType.AndAlso ||
                            sqlBinaryExpression.OperatorType == ExpressionType.OrElse ||
                            sqlBinaryExpression.OperatorType == ExpressionType.Equal ||
                            sqlBinaryExpression.OperatorType == ExpressionType.NotEqual ||
                            sqlBinaryExpression.OperatorType == ExpressionType.GreaterThan ||
                            sqlBinaryExpression.OperatorType == ExpressionType.GreaterThanOrEqual ||
                            sqlBinaryExpression.OperatorType == ExpressionType.LessThan ||
                            sqlBinaryExpression.OperatorType == ExpressionType.LessThanOrEqual;

            return(ApplyConversion(sqlBinaryExpression, condition));
        }
Exemplo n.º 3
0
        protected override Expression VisitSqlBinary(SqlBinaryExpression sqlBinaryExpression)
        {
            var left  = Visit(sqlBinaryExpression.Left) as SqlExpression;
            var right = Visit(sqlBinaryExpression.Right) as SqlExpression;

            return(sqlBinaryExpression.Update(left, right));
        }
Exemplo n.º 4
0
        private Expression VisitSqlBinaryExpression(SqlBinaryExpression sqlBinaryExpression)
        {
            var newLeft  = (SqlExpression)Visit(sqlBinaryExpression.Left);
            var newRight = (SqlExpression)Visit(sqlBinaryExpression.Right);

            if (sqlBinaryExpression.OperatorType == ExpressionType.AndAlso ||
                sqlBinaryExpression.OperatorType == ExpressionType.OrElse)
            {
                var newLeftConstant  = newLeft as SqlConstantExpression;
                var newRightConstant = newRight as SqlConstantExpression;

                // true && a -> a
                // true || a -> true
                // false && a -> false
                // false || a -> a
                if (newLeftConstant != null)
                {
                    return(sqlBinaryExpression.OperatorType == ExpressionType.AndAlso
                        ? (bool)newLeftConstant.Value
                            ? newRight
                            : newLeftConstant
                        : (bool)newLeftConstant.Value
                            ? newLeftConstant
                            : newRight);
                }
                else if (newRightConstant != null)
                {
                    // a && true -> a
                    // a || true -> true
                    // a && false -> false
                    // a || false -> a
                    return(sqlBinaryExpression.OperatorType == ExpressionType.AndAlso
                        ? (bool)newRightConstant.Value
                            ? newLeft
                            : newRightConstant
                        : (bool)newRightConstant.Value
                            ? newRightConstant
                            : newLeft);
                }

                return(sqlBinaryExpression.Update(newLeft, newRight));
            }

            return(sqlBinaryExpression.Update(newLeft, newRight));
        }
        protected override Expression VisitSqlBinary(SqlBinaryExpression sqlBinaryExpression)
        {
            var parentOptimize        = _optimize;
            var columnExpression      = sqlBinaryExpression.Left as ColumnExpression ?? sqlBinaryExpression.Right as ColumnExpression;
            var sqlConstantExpression = sqlBinaryExpression.Left as SqlConstantExpression ?? sqlBinaryExpression.Right as SqlConstantExpression;

            // Optimize translation of the following expressions:
            //     context.Table.Where(t => t.BoolColumn == true)
            //         translate to: `boolColumn` = TRUE
            //         instead of:   (`boolColumn` = TRUE) = TRUE
            //     context.Table.Where(t => t.BoolColumn == false)
            //         translate to: `boolColumn` = FALSE
            //         instead of:   (`boolColumn` = TRUE) = FALSE
            //     context.Table.Where(t => t.BoolColumn != true)
            //         translate to: `boolColumn` <> TRUE
            //         instead of:   (`boolColumn` = TRUE) <> TRUE
            //     context.Table.Where(t => t.BoolColumn != false)
            //         translate to: `boolColumn` <> FALSE
            //         instead of:   (`boolColumn` = TRUE) <> FALSE
            if (_optimize &&
                (sqlBinaryExpression.OperatorType == ExpressionType.Equal || sqlBinaryExpression.OperatorType == ExpressionType.NotEqual) &&
                columnExpression != null &&
                sqlConstantExpression != null &&
                columnExpression.TypeMapping is MySqlBoolTypeMapping &&
                columnExpression.Type == typeof(bool) &&
                sqlConstantExpression.TypeMapping is MySqlBoolTypeMapping &&
                sqlConstantExpression.Type == typeof(bool))
            {
                _optimize = false;
            }

            var newLeft  = (SqlExpression)Visit(sqlBinaryExpression.Left);
            var newRight = (SqlExpression)Visit(sqlBinaryExpression.Right);

            _optimize = parentOptimize;

            sqlBinaryExpression = sqlBinaryExpression.Update(newLeft, newRight);

            var condition = sqlBinaryExpression.OperatorType == ExpressionType.AndAlso ||
                            sqlBinaryExpression.OperatorType == ExpressionType.OrElse ||
                            sqlBinaryExpression.OperatorType == ExpressionType.Equal ||
                            sqlBinaryExpression.OperatorType == ExpressionType.NotEqual ||
                            sqlBinaryExpression.OperatorType == ExpressionType.GreaterThan ||
                            sqlBinaryExpression.OperatorType == ExpressionType.GreaterThanOrEqual ||
                            sqlBinaryExpression.OperatorType == ExpressionType.LessThan ||
                            sqlBinaryExpression.OperatorType == ExpressionType.LessThanOrEqual;

            return(ApplyConversion(sqlBinaryExpression, condition));
        }
Exemplo n.º 6
0
        private SqlBinaryExpression VisitSqlBinaryExpression(SqlBinaryExpression sqlBinaryExpression)
        {
            _isNullable = false;
            var newLeft      = (SqlExpression)Visit(sqlBinaryExpression.Left);
            var leftNullable = _isNullable;

            _isNullable = false;
            var newRight      = (SqlExpression)Visit(sqlBinaryExpression.Right);
            var rightNullable = _isNullable;

            if (sqlBinaryExpression.OperatorType == ExpressionType.Coalesce)
            {
                _isNullable = leftNullable && rightNullable;

                return(sqlBinaryExpression.Update(newLeft, newRight));
            }

            if (sqlBinaryExpression.OperatorType == ExpressionType.Equal ||
                sqlBinaryExpression.OperatorType == ExpressionType.NotEqual)
            {
                var leftUnary  = newLeft as SqlUnaryExpression;
                var rightUnary = newRight as SqlUnaryExpression;

                var leftNegated  = leftUnary?.OperatorType == ExpressionType.Not;
                var rightNegated = rightUnary?.OperatorType == ExpressionType.Not;

                if (leftNegated)
                {
                    newLeft = leftUnary.Operand;
                }

                if (rightNegated)
                {
                    newRight = rightUnary.Operand;
                }

                // TODO: optimize this by looking at subcomponents, e.g. f(a, b) == null <=> a == null || b == null
                var leftIsNull  = _sqlExpressionFactory.IsNull(newLeft);
                var rightIsNull = _sqlExpressionFactory.IsNull(newRight);

                // doing a full null semantics rewrite - removing all nulls from truth table
                _isNullable = false;

                if (sqlBinaryExpression.OperatorType == ExpressionType.Equal)
                {
                    if (!leftNullable && !rightNullable)
                    {
                        // a == b <=> !a == !b -> a == b
                        // !a == b <=> a == !b -> a != b
                        return(leftNegated == rightNegated
                            ? _sqlExpressionFactory.Equal(newLeft, newRight)
                            : _sqlExpressionFactory.NotEqual(newLeft, newRight));
                    }

                    if (leftNullable && rightNullable)
                    {
                        // ?a == ?b <=> !(?a) == !(?b) -> [(a == b) && (a != null && b != null)] || (a == null && b == null))
                        // !(?a) == ?b <=> ?a == !(?b) -> [(a != b) && (a != null && b != null)] || (a == null && b == null)
                        return(leftNegated == rightNegated
                            ? ExpandNullableEqualNullable(newLeft, newRight, leftIsNull, rightIsNull)
                            : ExpandNegatedNullableEqualNullable(newLeft, newRight, leftIsNull, rightIsNull));
                    }

                    if (leftNullable && !rightNullable)
                    {
                        // ?a == b <=> !(?a) == !b -> (a == b) && (a != null)
                        // !(?a) == b <=> ?a == !b -> (a != b) && (a != null)
                        return(leftNegated == rightNegated
                            ? ExpandNullableEqualNonNullable(newLeft, newRight, leftIsNull)
                            : ExpandNegatedNullableEqualNonNullable(newLeft, newRight, leftIsNull));
                    }

                    if (rightNullable && !leftNullable)
                    {
                        // a == ?b <=> !a == !(?b) -> (a == b) && (b != null)
                        // !a == ?b <=> a == !(?b) -> (a != b) && (b != null)
                        return(leftNegated == rightNegated
                            ? ExpandNullableEqualNonNullable(newLeft, newRight, rightIsNull)
                            : ExpandNegatedNullableEqualNonNullable(newLeft, newRight, rightIsNull));
                    }
                }

                if (sqlBinaryExpression.OperatorType == ExpressionType.NotEqual)
                {
                    if (!leftNullable && !rightNullable)
                    {
                        // a != b <=> !a != !b -> a != b
                        // !a != b <=> a != !b -> a == b
                        return(leftNegated == rightNegated
                            ? _sqlExpressionFactory.NotEqual(newLeft, newRight)
                            : _sqlExpressionFactory.Equal(newLeft, newRight));
                    }

                    if (leftNullable && rightNullable)
                    {
                        // ?a != ?b <=> !(?a) != !(?b) -> [(a != b) || (a == null || b == null)] && (a != null || b != null)
                        // !(?a) != ?b <=> ?a != !(?b) -> [(a == b) || (a == null || b == null)] && (a != null || b != null)
                        return(leftNegated == rightNegated
                            ? ExpandNullableNotEqualNullable(newLeft, newRight, leftIsNull, rightIsNull)
                            : ExpandNegatedNullableNotEqualNullable(newLeft, newRight, leftIsNull, rightIsNull));
                    }

                    if (leftNullable)
                    {
                        // ?a != b <=> !(?a) != !b -> (a != b) || (a == null)
                        // !(?a) != b <=> ?a != !b -> (a == b) || (a == null)
                        return(leftNegated == rightNegated
                            ? ExpandNullableNotEqualNonNullable(newLeft, newRight, leftIsNull)
                            : ExpandNegatedNullableNotEqualNonNullable(newLeft, newRight, leftIsNull));
                    }

                    if (rightNullable)
                    {
                        // a != ?b <=> !a != !(?b) -> (a != b) || (b == null)
                        // !a != ?b <=> a != !(?b) -> (a == b) || (b == null)
                        return(leftNegated == rightNegated
                            ? ExpandNullableNotEqualNonNullable(newLeft, newRight, rightIsNull)
                            : ExpandNegatedNullableNotEqualNonNullable(newLeft, newRight, rightIsNull));
                    }
                }
            }

            _isNullable = leftNullable || rightNullable;

            return(sqlBinaryExpression.Update(newLeft, newRight));
        }
Exemplo n.º 7
0
        private Expression VisitSqlBinaryExpression(SqlBinaryExpression sqlBinaryExpression)
        {
            var newLeft  = (SqlExpression)Visit(sqlBinaryExpression.Left);
            var newRight = (SqlExpression)Visit(sqlBinaryExpression.Right);

            if (sqlBinaryExpression.OperatorType == ExpressionType.AndAlso ||
                sqlBinaryExpression.OperatorType == ExpressionType.OrElse)
            {
                var newLeftConstant  = newLeft as SqlConstantExpression;
                var newRightConstant = newRight as SqlConstantExpression;

                // true && a -> a
                // true || a -> true
                // false && a -> false
                // false || a -> a
                if (newLeftConstant != null)
                {
                    return(sqlBinaryExpression.OperatorType == ExpressionType.AndAlso
                        ? (bool)newLeftConstant.Value
                            ? newRight
                            : newLeftConstant
                        : (bool)newLeftConstant.Value
                            ? newLeftConstant
                            : newRight);
                }
                else if (newRightConstant != null)
                {
                    // a && true -> a
                    // a || true -> true
                    // a && false -> false
                    // a || false -> a
                    return(sqlBinaryExpression.OperatorType == ExpressionType.AndAlso
                        ? (bool)newRightConstant.Value
                            ? newLeft
                            : newRightConstant
                        : (bool)newRightConstant.Value
                            ? newRightConstant
                            : newLeft);
                }

                return(sqlBinaryExpression.Update(newLeft, newRight));
            }

            // those optimizations are only valid in 2-value logic
            // they are safe to do here because null semantics removes possibility of nulls in the tree
            // however if we decide to do "partial" null semantics (that doesn't distinguish between NULL and FALSE, e.g. for predicates)
            // we need to be extra careful here
            if (!_useRelationalNulls &&
                (sqlBinaryExpression.OperatorType == ExpressionType.Equal || sqlBinaryExpression.OperatorType == ExpressionType.NotEqual))
            {
                // op(a, b) == true -> op(a, b)
                // op(a, b) != false -> op(a, b)
                // op(a, b) == false -> !op(a, b)
                // op(a, b) != true -> !op(a, b)
                var constant = sqlBinaryExpression.Left as SqlConstantExpression ?? sqlBinaryExpression.Right as SqlConstantExpression;
                var binary   = sqlBinaryExpression.Left as SqlBinaryExpression ?? sqlBinaryExpression.Right as SqlBinaryExpression;
                if (constant != null && binary != null && TryNegate(binary.OperatorType, out var negated))
                {
                    return((bool)constant.Value == (sqlBinaryExpression.OperatorType == ExpressionType.Equal)
                        ? binary
                        : _sqlExpressionFactory.MakeBinary(
                               negated,
                               sqlBinaryExpression.Left,
                               sqlBinaryExpression.Right,
                               sqlBinaryExpression.TypeMapping));
                }
            }

            return(sqlBinaryExpression.Update(newLeft, newRight));
        }
Exemplo n.º 8
0
        protected override Expression VisitSqlBinary(SqlBinaryExpression sqlBinaryExpression)
        {
            Check.NotNull(sqlBinaryExpression, nameof(sqlBinaryExpression));

            _isNullable = false;
            var canOptimize = _canOptimize;

            // for SqlServer we could also allow optimize on children of ExpressionType.Equal
            // because they get converted to CASE blocks anyway, but for other providers it's incorrect
            // once/if null semantics optimizations are provider-specific we can enable it
            _canOptimize = _canOptimize && (sqlBinaryExpression.OperatorType == ExpressionType.AndAlso ||
                                            sqlBinaryExpression.OperatorType == ExpressionType.OrElse);

            var nonNullableColumns = new List <ColumnExpression>();

            if (sqlBinaryExpression.OperatorType == ExpressionType.AndAlso)
            {
                nonNullableColumns = FindNonNullableColumns(sqlBinaryExpression.Left);
            }

            var newLeft      = (SqlExpression)Visit(sqlBinaryExpression.Left);
            var leftNullable = _isNullable;

            _isNullable = false;
            if (nonNullableColumns.Count > 0)
            {
                _nonNullableColumns.AddRange(nonNullableColumns);
            }

            var newRight      = (SqlExpression)Visit(sqlBinaryExpression.Right);
            var rightNullable = _isNullable;

            foreach (var nonNullableColumn in nonNullableColumns)
            {
                _nonNullableColumns.Remove(nonNullableColumn);
            }

            if (sqlBinaryExpression.OperatorType == ExpressionType.Coalesce)
            {
                _isNullable  = leftNullable && rightNullable;
                _canOptimize = canOptimize;

                return(sqlBinaryExpression.Update(newLeft, newRight));
            }

            if (sqlBinaryExpression.OperatorType == ExpressionType.Add &&
                sqlBinaryExpression.Type == typeof(string))
            {
                if (leftNullable)
                {
                    newLeft = newLeft is SqlConstantExpression
                        ? _sqlExpressionFactory.Constant(string.Empty)
                        : newLeft is ColumnExpression || newLeft is SqlParameterExpression
                            ? _sqlExpressionFactory.Coalesce(newLeft, _sqlExpressionFactory.Constant(string.Empty))
                            : newLeft;
                }

                if (rightNullable)
                {
                    newRight = newRight is SqlConstantExpression
                        ? _sqlExpressionFactory.Constant(string.Empty)
                        : newRight is ColumnExpression || newRight is SqlParameterExpression
                            ? _sqlExpressionFactory.Coalesce(newRight, _sqlExpressionFactory.Constant(string.Empty))
                            : newRight;
                }

                return(sqlBinaryExpression.Update(newLeft, newRight));
            }

            if (sqlBinaryExpression.OperatorType == ExpressionType.Equal ||
                sqlBinaryExpression.OperatorType == ExpressionType.NotEqual)
            {
                var leftConstantNull  = newLeft is SqlConstantExpression leftConstant && leftConstant.Value == null;
                var rightConstantNull = newRight is SqlConstantExpression rightConstant && rightConstant.Value == null;

                // a == null -> a IS NULL
                // a != null -> a IS NOT NULL
                if (rightConstantNull)
                {
                    _isNullable  = false;
                    _canOptimize = canOptimize;

                    return(sqlBinaryExpression.OperatorType == ExpressionType.Equal
                        ? _sqlExpressionFactory.IsNull(newLeft)
                        : _sqlExpressionFactory.IsNotNull(newLeft));
                }

                // null == a -> a IS NULL
                // null != a -> a IS NOT NULL
                if (leftConstantNull)
                {
                    _isNullable  = false;
                    _canOptimize = canOptimize;

                    return(sqlBinaryExpression.OperatorType == ExpressionType.Equal
                        ? _sqlExpressionFactory.IsNull(newRight)
                        : _sqlExpressionFactory.IsNotNull(newRight));
                }

                var leftUnary  = newLeft as SqlUnaryExpression;
                var rightUnary = newRight as SqlUnaryExpression;

                var leftNegated  = leftUnary?.IsLogicalNot() == true;
                var rightNegated = rightUnary?.IsLogicalNot() == true;

                if (leftNegated)
                {
                    newLeft = leftUnary.Operand;
                }

                if (rightNegated)
                {
                    newRight = rightUnary.Operand;
                }

                var leftIsNull  = _sqlExpressionFactory.IsNull(newLeft);
                var rightIsNull = _sqlExpressionFactory.IsNull(newRight);

                // optimized expansion which doesn't distinguish between null and false
                if (canOptimize &&
                    sqlBinaryExpression.OperatorType == ExpressionType.Equal &&
                    !leftNegated &&
                    !rightNegated)
                {
                    // when we use optimized form, the result can still be nullable
                    if (leftNullable && rightNullable)
                    {
                        _isNullable  = true;
                        _canOptimize = canOptimize;

                        return(_sqlExpressionFactory.OrElse(
                                   _sqlExpressionFactory.Equal(newLeft, newRight),
                                   _sqlExpressionFactory.AndAlso(leftIsNull, rightIsNull)));
                    }

                    if ((leftNullable && !rightNullable) ||
                        (!leftNullable && rightNullable))
                    {
                        _isNullable  = true;
                        _canOptimize = canOptimize;

                        return(_sqlExpressionFactory.Equal(newLeft, newRight));
                    }
                }

                // doing a full null semantics rewrite - removing all nulls from truth table
                // this will NOT be correct once we introduce simplified null semantics
                _isNullable  = false;
                _canOptimize = canOptimize;

                if (sqlBinaryExpression.OperatorType == ExpressionType.Equal)
                {
                    if (!leftNullable &&
                        !rightNullable)
                    {
                        // a == b <=> !a == !b -> a == b
                        // !a == b <=> a == !b -> a != b
                        return(leftNegated == rightNegated
                            ? _sqlExpressionFactory.Equal(newLeft, newRight)
                            : _sqlExpressionFactory.NotEqual(newLeft, newRight));
                    }

                    if (leftNullable && rightNullable)
                    {
                        // ?a == ?b <=> !(?a) == !(?b) -> [(a == b) && (a != null && b != null)] || (a == null && b == null))
                        // !(?a) == ?b <=> ?a == !(?b) -> [(a != b) && (a != null && b != null)] || (a == null && b == null)
                        return(leftNegated == rightNegated
                            ? ExpandNullableEqualNullable(newLeft, newRight, leftIsNull, rightIsNull)
                            : ExpandNegatedNullableEqualNullable(newLeft, newRight, leftIsNull, rightIsNull));
                    }

                    if (leftNullable && !rightNullable)
                    {
                        // ?a == b <=> !(?a) == !b -> (a == b) && (a != null)
                        // !(?a) == b <=> ?a == !b -> (a != b) && (a != null)
                        return(leftNegated == rightNegated
                            ? ExpandNullableEqualNonNullable(newLeft, newRight, leftIsNull)
                            : ExpandNegatedNullableEqualNonNullable(newLeft, newRight, leftIsNull));
                    }

                    if (rightNullable && !leftNullable)
                    {
                        // a == ?b <=> !a == !(?b) -> (a == b) && (b != null)
                        // !a == ?b <=> a == !(?b) -> (a != b) && (b != null)
                        return(leftNegated == rightNegated
                            ? ExpandNullableEqualNonNullable(newLeft, newRight, rightIsNull)
                            : ExpandNegatedNullableEqualNonNullable(newLeft, newRight, rightIsNull));
                    }
                }

                if (sqlBinaryExpression.OperatorType == ExpressionType.NotEqual)
                {
                    if (!leftNullable &&
                        !rightNullable)
                    {
                        // a != b <=> !a != !b -> a != b
                        // !a != b <=> a != !b -> a == b
                        return(leftNegated == rightNegated
                            ? _sqlExpressionFactory.NotEqual(newLeft, newRight)
                            : _sqlExpressionFactory.Equal(newLeft, newRight));
                    }

                    if (leftNullable && rightNullable)
                    {
                        // ?a != ?b <=> !(?a) != !(?b) -> [(a != b) || (a == null || b == null)] && (a != null || b != null)
                        // !(?a) != ?b <=> ?a != !(?b) -> [(a == b) || (a == null || b == null)] && (a != null || b != null)
                        return(leftNegated == rightNegated
                            ? ExpandNullableNotEqualNullable(newLeft, newRight, leftIsNull, rightIsNull)
                            : ExpandNegatedNullableNotEqualNullable(newLeft, newRight, leftIsNull, rightIsNull));
                    }

                    if (leftNullable)
                    {
                        // ?a != b <=> !(?a) != !b -> (a != b) || (a == null)
                        // !(?a) != b <=> ?a != !b -> (a == b) || (a == null)
                        return(leftNegated == rightNegated
                            ? ExpandNullableNotEqualNonNullable(newLeft, newRight, leftIsNull)
                            : ExpandNegatedNullableNotEqualNonNullable(newLeft, newRight, leftIsNull));
                    }

                    if (rightNullable)
                    {
                        // a != ?b <=> !a != !(?b) -> (a != b) || (b == null)
                        // !a != ?b <=> a != !(?b) -> (a == b) || (b == null)
                        return(leftNegated == rightNegated
                            ? ExpandNullableNotEqualNonNullable(newLeft, newRight, rightIsNull)
                            : ExpandNegatedNullableNotEqualNonNullable(newLeft, newRight, rightIsNull));
                    }
                }
            }

            _isNullable  = leftNullable || rightNullable;
            _canOptimize = canOptimize;

            return(sqlBinaryExpression.Update(newLeft, newRight));
        }
 protected override Expression VisitSqlBinary(SqlBinaryExpression x)
 {
     return(x?.Update(x.Left.VisitNode(this), x.Right.VisitNode(this)));
 }
Exemplo n.º 10
0
        protected override Expression VisitSqlBinary(SqlBinaryExpression sqlBinaryExpression)
        {
            Check.NotNull(sqlBinaryExpression, nameof(sqlBinaryExpression));

            var parentOptimize        = _optimize;
            var columnExpression      = sqlBinaryExpression.Left as ColumnExpression ?? sqlBinaryExpression.Right as ColumnExpression;
            var sqlConstantExpression = sqlBinaryExpression.Left as SqlConstantExpression ?? sqlBinaryExpression.Right as SqlConstantExpression;

            // TODO: Simplify for .NET 5, due to the already existing bool expression optimizations performed by `SqlNullabilityProcessor`.
            //       This custom logic can probably be removed completely.
            //       See `GearsOfWarQueryMySqlTest`.

            // Optimize translation of the following expressions:
            //     context.Table.Where(t => t.BoolColumn == true)
            //         translate to: `boolColumn` = TRUE
            //         instead of:   (`boolColumn` = TRUE) = TRUE
            //     context.Table.Where(t => t.BoolColumn == false)
            //         translate to: `boolColumn` = FALSE
            //         instead of:   (`boolColumn` = TRUE) = FALSE
            //     context.Table.Where(t => t.BoolColumn != true)
            //         translate to: `boolColumn` <> TRUE
            //         instead of:   (`boolColumn` = TRUE) <> TRUE
            //     context.Table.Where(t => t.BoolColumn != false)
            //         translate to: `boolColumn` <> FALSE
            //         instead of:   (`boolColumn` = TRUE) <> FALSE
            if (_optimize &&
                (sqlBinaryExpression.OperatorType == ExpressionType.Equal || sqlBinaryExpression.OperatorType == ExpressionType.NotEqual) &&
                columnExpression != null &&
                sqlConstantExpression != null &&
                columnExpression.TypeMapping is MySqlBoolTypeMapping &&
                columnExpression.Type == typeof(bool) &&
                sqlConstantExpression.TypeMapping is MySqlBoolTypeMapping &&
                sqlConstantExpression.Type == typeof(bool))
            {
                _optimize = false;
            }
            else
            {
                switch (sqlBinaryExpression.OperatorType)
                {
                // Only logical operations need conditions on both sides
                case ExpressionType.AndAlso:
                case ExpressionType.OrElse:
                    _optimize = true;
                    break;

                default:
                    _optimize = false;
                    break;
                }
            }

            var newLeft  = (SqlExpression)Visit(sqlBinaryExpression.Left);
            var newRight = (SqlExpression)Visit(sqlBinaryExpression.Right);

            _optimize = parentOptimize;

            sqlBinaryExpression = sqlBinaryExpression.Update(newLeft, newRight);

            var condition = sqlBinaryExpression.OperatorType == ExpressionType.AndAlso ||
                            sqlBinaryExpression.OperatorType == ExpressionType.OrElse ||
                            sqlBinaryExpression.OperatorType == ExpressionType.Equal ||
                            sqlBinaryExpression.OperatorType == ExpressionType.NotEqual ||
                            sqlBinaryExpression.OperatorType == ExpressionType.GreaterThan ||
                            sqlBinaryExpression.OperatorType == ExpressionType.GreaterThanOrEqual ||
                            sqlBinaryExpression.OperatorType == ExpressionType.LessThan ||
                            sqlBinaryExpression.OperatorType == ExpressionType.LessThanOrEqual;

            return(ApplyConversion(sqlBinaryExpression, condition));
        }
        private SqlBinaryExpression VisitSqlBinaryExpression(SqlBinaryExpression sqlBinaryExpression)
        {
            _isNullable = false;

            var nonNullableColumns = new List <ColumnExpression>();

            if (sqlBinaryExpression.OperatorType == ExpressionType.AndAlso)
            {
                nonNullableColumns = FindNonNullableColumns(sqlBinaryExpression.Left);
            }

            var newLeft      = (SqlExpression)Visit(sqlBinaryExpression.Left);
            var leftNullable = _isNullable;

            _isNullable = false;
            if (nonNullableColumns.Count > 0)
            {
                _nonNullableColumns.AddRange(nonNullableColumns);
            }

            var newRight      = (SqlExpression)Visit(sqlBinaryExpression.Right);
            var rightNullable = _isNullable;

            foreach (var nonNullableColumn in nonNullableColumns)
            {
                _nonNullableColumns.Remove(nonNullableColumn);
            }

            if (sqlBinaryExpression.OperatorType == ExpressionType.Coalesce)
            {
                _isNullable = leftNullable && rightNullable;

                return(sqlBinaryExpression.Update(newLeft, newRight));
            }

            if (sqlBinaryExpression.OperatorType == ExpressionType.Equal ||
                sqlBinaryExpression.OperatorType == ExpressionType.NotEqual)
            {
                var leftUnary  = newLeft as SqlUnaryExpression;
                var rightUnary = newRight as SqlUnaryExpression;

                var leftNegated  = leftUnary?.OperatorType == ExpressionType.Not;
                var rightNegated = rightUnary?.OperatorType == ExpressionType.Not;

                if (leftNegated)
                {
                    newLeft = leftUnary.Operand;
                }

                if (rightNegated)
                {
                    newRight = rightUnary.Operand;
                }

                var leftIsNull  = _sqlExpressionFactory.IsNull(newLeft);
                var rightIsNull = _sqlExpressionFactory.IsNull(newRight);

                // doing a full null semantics rewrite - removing all nulls from truth table
                // this will NOT be correct once we introduce simplified null semantics
                _isNullable = false;

                if (sqlBinaryExpression.OperatorType == ExpressionType.Equal)
                {
                    if (!leftNullable &&
                        !rightNullable)
                    {
                        // a == b <=> !a == !b -> a == b
                        // !a == b <=> a == !b -> a != b
                        return(leftNegated == rightNegated
                            ? _sqlExpressionFactory.Equal(newLeft, newRight)
                            : _sqlExpressionFactory.NotEqual(newLeft, newRight));
                    }

                    if (leftNullable && rightNullable)
                    {
                        // ?a == ?b <=> !(?a) == !(?b) -> [(a == b) && (a != null && b != null)] || (a == null && b == null))
                        // !(?a) == ?b <=> ?a == !(?b) -> [(a != b) && (a != null && b != null)] || (a == null && b == null)
                        return(leftNegated == rightNegated
                            ? ExpandNullableEqualNullable(newLeft, newRight, leftIsNull, rightIsNull)
                            : ExpandNegatedNullableEqualNullable(newLeft, newRight, leftIsNull, rightIsNull));
                    }

                    if (leftNullable && !rightNullable)
                    {
                        // ?a == b <=> !(?a) == !b -> (a == b) && (a != null)
                        // !(?a) == b <=> ?a == !b -> (a != b) && (a != null)
                        return(leftNegated == rightNegated
                            ? ExpandNullableEqualNonNullable(newLeft, newRight, leftIsNull)
                            : ExpandNegatedNullableEqualNonNullable(newLeft, newRight, leftIsNull));
                    }

                    if (rightNullable && !leftNullable)
                    {
                        // a == ?b <=> !a == !(?b) -> (a == b) && (b != null)
                        // !a == ?b <=> a == !(?b) -> (a != b) && (b != null)
                        return(leftNegated == rightNegated
                            ? ExpandNullableEqualNonNullable(newLeft, newRight, rightIsNull)
                            : ExpandNegatedNullableEqualNonNullable(newLeft, newRight, rightIsNull));
                    }
                }

                if (sqlBinaryExpression.OperatorType == ExpressionType.NotEqual)
                {
                    if (!leftNullable &&
                        !rightNullable)
                    {
                        // a != b <=> !a != !b -> a != b
                        // !a != b <=> a != !b -> a == b
                        return(leftNegated == rightNegated
                            ? _sqlExpressionFactory.NotEqual(newLeft, newRight)
                            : _sqlExpressionFactory.Equal(newLeft, newRight));
                    }

                    if (leftNullable && rightNullable)
                    {
                        // ?a != ?b <=> !(?a) != !(?b) -> [(a != b) || (a == null || b == null)] && (a != null || b != null)
                        // !(?a) != ?b <=> ?a != !(?b) -> [(a == b) || (a == null || b == null)] && (a != null || b != null)
                        return(leftNegated == rightNegated
                            ? ExpandNullableNotEqualNullable(newLeft, newRight, leftIsNull, rightIsNull)
                            : ExpandNegatedNullableNotEqualNullable(newLeft, newRight, leftIsNull, rightIsNull));
                    }

                    if (leftNullable)
                    {
                        // ?a != b <=> !(?a) != !b -> (a != b) || (a == null)
                        // !(?a) != b <=> ?a != !b -> (a == b) || (a == null)
                        return(leftNegated == rightNegated
                            ? ExpandNullableNotEqualNonNullable(newLeft, newRight, leftIsNull)
                            : ExpandNegatedNullableNotEqualNonNullable(newLeft, newRight, leftIsNull));
                    }

                    if (rightNullable)
                    {
                        // a != ?b <=> !a != !(?b) -> (a != b) || (b == null)
                        // !a != ?b <=> a != !(?b) -> (a == b) || (b == null)
                        return(leftNegated == rightNegated
                            ? ExpandNullableNotEqualNonNullable(newLeft, newRight, rightIsNull)
                            : ExpandNegatedNullableNotEqualNonNullable(newLeft, newRight, rightIsNull));
                    }
                }
            }

            _isNullable = leftNullable || rightNullable;

            return(sqlBinaryExpression.Update(newLeft, newRight));
        }