/// <summary>
        ///     This is an internal API that supports the Entity Framework Core infrastructure and not subject to
        ///     the same compatibility standards as public APIs. It may be changed or removed without notice in
        ///     any release. You should only use it directly in your code with extreme caution and knowing that
        ///     doing so can result in application failures when updating to a new Entity Framework Core release.
        /// </summary>
        protected override Expression VisitBinary(BinaryExpression binaryExpression)
        {
            Check.NotNull(binaryExpression, nameof(binaryExpression));

            if (binaryExpression.NodeType == ExpressionType.Coalesce)
            {
                var ifTrue  = binaryExpression.Left;
                var ifFalse = binaryExpression.Right;
                if (ifTrue.Type != ifFalse.Type)
                {
                    ifFalse = Expression.Convert(ifFalse, ifTrue.Type);
                }

                return(Visit(
                           Expression.Condition(
                               Expression.NotEqual(ifTrue, Expression.Constant(null, ifTrue.Type)),
                               ifTrue,
                               ifFalse)));
            }

            var left  = TryRemoveImplicitConvert(binaryExpression.Left);
            var right = TryRemoveImplicitConvert(binaryExpression.Right);

            left  = Visit(left);
            right = Visit(right);

            return(TranslationFailed(binaryExpression.Left, left, out var sqlLeft) ||
                   TranslationFailed(binaryExpression.Right, right, out var sqlRight)
                ? null
                : _sqlExpressionFactory.MakeBinary(
                       binaryExpression.NodeType,
                       sqlLeft,
                       sqlRight,
                       null));
        }
        protected override Expression VisitBinary(BinaryExpression binaryExpression)
        {
            if (binaryExpression.Left.Type == typeof(AnonymousObject) &&
                binaryExpression.NodeType == ExpressionType.Equal)
            {
                return(Visit(ConvertAnonymousObjectEqualityComparison(binaryExpression)));
            }

            var left  = TryRemoveImplicitConvert(binaryExpression.Left);
            var right = TryRemoveImplicitConvert(binaryExpression.Right);

            left  = Visit(left);
            right = Visit(right);

            if (TranslationFailed(binaryExpression.Left, left) ||
                TranslationFailed(binaryExpression.Right, right))
            {
                return(null);
            }

            return(_sqlExpressionFactory.MakeBinary(
                       binaryExpression.NodeType,
                       (SqlExpression)left,
                       (SqlExpression)right,
                       null));
        }
コード例 #3
0
        protected override Expression VisitBinary(BinaryExpression binaryExpression)
        {
            if (binaryExpression.NodeType == ExpressionType.Coalesce)
            {
                return(Visit(Expression.Condition(
                                 Expression.NotEqual(binaryExpression.Left, Expression.Constant(null, binaryExpression.Left.Type)),
                                 binaryExpression.Left,
                                 binaryExpression.Right)));
            }

            var left  = TryRemoveImplicitConvert(binaryExpression.Left);
            var right = TryRemoveImplicitConvert(binaryExpression.Right);

            left  = Visit(left);
            right = Visit(right);

            if (TranslationFailed(binaryExpression.Left, left) ||
                TranslationFailed(binaryExpression.Right, right))
            {
                return(null);
            }

            return(_sqlExpressionFactory.MakeBinary(
                       binaryExpression.NodeType,
                       (SqlExpression)left,
                       (SqlExpression)right,
                       null));
        }
コード例 #4
0
        protected override Expression VisitSqlUnary(SqlUnaryExpression sqlUnaryExpression)
        {
            Check.NotNull(sqlUnaryExpression, nameof(sqlUnaryExpression));

            var  parentOptimize = _optimize;
            bool resultCondition;

            switch (sqlUnaryExpression.OperatorType)
            {
            case ExpressionType.Not
                when sqlUnaryExpression.Type == typeof(bool):
                _optimize       = true;
                resultCondition = true;
                break;

            case ExpressionType.Not:
                _optimize       = false;
                resultCondition = false;
                break;

            case ExpressionType.Convert:
            case ExpressionType.Negate:
                _optimize       = false;
                resultCondition = false;
                break;

            case ExpressionType.Equal:
            case ExpressionType.NotEqual:
                _optimize       = false;
                resultCondition = true;
                break;

            default:
                throw new InvalidOperationException("Unknown operator type encountered in SqlUnaryExpression.");
            }

            SqlExpression expression;

            // 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)
            //         translate to: `boolColumn` = FALSE
            //         instead of:   NOT(`boolColumn` = TRUE)
            // Translating to "NOT(`boolColumn`)" would not use indices in MySQL 5.7.
            if (sqlUnaryExpression.OperatorType == ExpressionType.Not &&
                sqlUnaryExpression.Operand is ColumnExpression columnExpression &&
                columnExpression.TypeMapping is MySqlBoolTypeMapping &&
                columnExpression.Type == typeof(bool))
            {
                _optimize = false;

                expression = _sqlExpressionFactory.MakeBinary(
                    ExpressionType.Equal,
                    (SqlExpression)Visit(sqlUnaryExpression.Operand),
                    _sqlExpressionFactory.Constant(false),
                    sqlUnaryExpression.TypeMapping);
            }
コード例 #5
0
        protected override Expression VisitBinary(BinaryExpression binaryExpression)
        {
            Check.NotNull(binaryExpression, nameof(binaryExpression));

            if (binaryExpression.Left.Type == typeof(AnonymousObject) &&
                binaryExpression.NodeType == ExpressionType.Equal)
            {
                return(Visit(ConvertAnonymousObjectEqualityComparison(binaryExpression)));
            }

            var uncheckedNodeTypeVariant = binaryExpression.NodeType switch
            {
                ExpressionType.AddChecked => ExpressionType.Add,
                ExpressionType.SubtractChecked => ExpressionType.Subtract,
                ExpressionType.MultiplyChecked => ExpressionType.Multiply,
                _ => binaryExpression.NodeType
            };

            var left  = TryRemoveImplicitConvert(binaryExpression.Left);
            var right = TryRemoveImplicitConvert(binaryExpression.Right);

            return(TranslationFailed(binaryExpression.Left, Visit(left), out var sqlLeft) ||
                   TranslationFailed(binaryExpression.Right, Visit(right), out var sqlRight)
                ? null
                : uncheckedNodeTypeVariant == ExpressionType.Coalesce
                    ? _sqlExpressionFactory.Coalesce(sqlLeft, sqlRight)
                    : (Expression)_sqlExpressionFactory.MakeBinary(
                       uncheckedNodeTypeVariant,
                       sqlLeft,
                       sqlRight,
                       null));
        }
コード例 #6
0
        /// <summary>
        ///     This is an internal API that supports the Entity Framework Core infrastructure and not subject to
        ///     the same compatibility standards as public APIs. It may be changed or removed without notice in
        ///     any release. You should only use it directly in your code with extreme caution and knowing that
        ///     doing so can result in application failures when updating to a new Entity Framework Core release.
        /// </summary>
        protected override Expression VisitBinary(BinaryExpression binaryExpression)
        {
            Check.NotNull(binaryExpression, nameof(binaryExpression));

            if (binaryExpression.NodeType == ExpressionType.Coalesce)
            {
                var ifTrue  = binaryExpression.Left;
                var ifFalse = binaryExpression.Right;
                if (ifTrue.Type != ifFalse.Type)
                {
                    ifFalse = Expression.Convert(ifFalse, ifTrue.Type);
                }

                return(Visit(
                           Expression.Condition(
                               Expression.NotEqual(ifTrue, Expression.Constant(null, ifTrue.Type)),
                               ifTrue,
                               ifFalse)));
            }

            var left  = TryRemoveImplicitConvert(binaryExpression.Left);
            var right = TryRemoveImplicitConvert(binaryExpression.Right);

            var visitedLeft  = Visit(left);
            var visitedRight = Visit(right);

            if ((binaryExpression.NodeType == ExpressionType.Equal ||
                 binaryExpression.NodeType == ExpressionType.NotEqual)
                // Visited expression could be null, We need to pass MemberInitExpression
                && TryRewriteEntityEquality(binaryExpression.NodeType, visitedLeft ?? left, visitedRight ?? right, out var result))
            {
                return(result);
            }

            if (binaryExpression.Method == _concatMethodInfo)
            {
                return(null);
            }

            var uncheckedNodeTypeVariant = binaryExpression.NodeType switch
            {
                ExpressionType.AddChecked => ExpressionType.Add,
                ExpressionType.SubtractChecked => ExpressionType.Subtract,
                ExpressionType.MultiplyChecked => ExpressionType.Multiply,
                _ => binaryExpression.NodeType
            };

            return(TranslationFailed(binaryExpression.Left, visitedLeft, out var sqlLeft) ||
                   TranslationFailed(binaryExpression.Right, visitedRight, out var sqlRight)
                ? null
                : _sqlExpressionFactory.MakeBinary(
                       uncheckedNodeTypeVariant,
                       sqlLeft,
                       sqlRight,
                       null));
        }
        protected override Expression VisitSqlUnary(SqlUnaryExpression sqlUnaryExpression)
        {
            var  parentOptimize = _optimize;
            bool resultCondition;

            switch (sqlUnaryExpression.OperatorType)
            {
            case ExpressionType.Not:
                _optimize       = true;
                resultCondition = true;
                break;

            case ExpressionType.Convert:
            case ExpressionType.Negate:
                _optimize       = false;
                resultCondition = false;
                break;

            case ExpressionType.Equal:
            case ExpressionType.NotEqual:
                _optimize       = false;
                resultCondition = true;
                break;

            default:
                throw new InvalidOperationException("Unknown operator type encountered in SqlUnaryExpression.");
            }

            SqlExpression expression;

            // Optimize translation of the following expressions:
            //     context.Table.Where(t => !t.BoolColumn)
            //         translate to: `boolColumn` = FALSE
            //         instead of:   NOT(`boolColumn` = TRUE)
            // Translating to "NOT(`boolColumn`)" would not use indices in MySQL 5.7.
            if (sqlUnaryExpression.OperatorType == ExpressionType.Not &&
                sqlUnaryExpression.Operand is ColumnExpression columnExpression &&
                columnExpression.TypeMapping is MySqlBoolTypeMapping &&
                columnExpression.Type == typeof(bool))
            {
                _optimize = false;

                expression = _sqlExpressionFactory.MakeBinary(
                    ExpressionType.Equal,
                    (SqlExpression)Visit(sqlUnaryExpression.Operand),
                    _sqlExpressionFactory.Constant(false),
                    sqlUnaryExpression.TypeMapping);
            }
        protected override Expression VisitBinary(BinaryExpression binaryExpression)
        {
            var left  = (SqlExpression)Visit(binaryExpression.Left);
            var right = (SqlExpression)Visit(binaryExpression.Right);

            if (TranslationFailed(binaryExpression.Left, left) ||
                TranslationFailed(binaryExpression.Right, right))
            {
                return(null);
            }

            return(_sqlExpressionFactory.MakeBinary(
                       binaryExpression.NodeType,
                       left,
                       right,
                       null));
        }
コード例 #9
0
        protected override Expression VisitBinary(BinaryExpression binaryExpression)
        {
            Check.NotNull(binaryExpression, nameof(binaryExpression));

            if (binaryExpression.Left.Type == typeof(object[]) &&
                binaryExpression.Left is NewArrayExpression &&
                binaryExpression.NodeType == ExpressionType.Equal)
            {
                return(Visit(ConvertObjectArrayEqualityComparison(binaryExpression)));
            }

            var left  = TryRemoveImplicitConvert(binaryExpression.Left);
            var right = TryRemoveImplicitConvert(binaryExpression.Right);

            var visitedLeft  = Visit(left);
            var visitedRight = Visit(right);

            if ((binaryExpression.NodeType == ExpressionType.Equal ||
                 binaryExpression.NodeType == ExpressionType.NotEqual)
                // Visited expression could be null, We need to pass MemberInitExpression
                && TryRewriteEntityEquality(binaryExpression.NodeType, visitedLeft ?? left, visitedRight ?? right, out var result))
            {
                return(result);
            }

            var uncheckedNodeTypeVariant = binaryExpression.NodeType switch
            {
                ExpressionType.AddChecked => ExpressionType.Add,
                ExpressionType.SubtractChecked => ExpressionType.Subtract,
                ExpressionType.MultiplyChecked => ExpressionType.Multiply,
                _ => binaryExpression.NodeType
            };

            return(TranslationFailed(binaryExpression.Left, visitedLeft, out var sqlLeft) ||
                   TranslationFailed(binaryExpression.Right, visitedRight, out var sqlRight)
                ? null
                : uncheckedNodeTypeVariant == ExpressionType.Coalesce
                    ? _sqlExpressionFactory.Coalesce(sqlLeft, sqlRight)
                    : (Expression)_sqlExpressionFactory.MakeBinary(
                       uncheckedNodeTypeVariant,
                       sqlLeft,
                       sqlRight,
                       null));
        }
        protected override Expression VisitBinary(BinaryExpression binaryExpression)
        {
            var left  = TryRemoveImplicitConvert(binaryExpression.Left);
            var right = TryRemoveImplicitConvert(binaryExpression.Right);

            left  = Visit(left);
            right = Visit(right);

            if (TranslationFailed(binaryExpression.Left, left) ||
                TranslationFailed(binaryExpression.Right, right))
            {
                return(null);
            }

            return(_sqlExpressionFactory.MakeBinary(
                       binaryExpression.NodeType,
                       (SqlExpression)left,
                       (SqlExpression)right,
                       null));
        }
コード例 #11
0
        protected override Expression VisitBinary(BinaryExpression binaryExpression)
        {
            Check.NotNull(binaryExpression, nameof(binaryExpression));

            if (binaryExpression.NodeType == ExpressionType.Coalesce)
            {
                var ifTrue  = binaryExpression.Left;
                var ifFalse = binaryExpression.Right;
                if (ifTrue.Type != ifFalse.Type)
                {
                    ifFalse = Expression.Convert(ifFalse, ifTrue.Type);
                }

                return(Visit(
                           Expression.Condition(
                               Expression.NotEqual(ifTrue, Expression.Constant(null, ifTrue.Type)),
                               ifTrue,
                               ifFalse)));
            }

            var left  = TryRemoveImplicitConvert(binaryExpression.Left);
            var right = TryRemoveImplicitConvert(binaryExpression.Right);

            // Remove convert-to-object nodes if both sides have them, or if the other side is null constant
            var isLeftConvertToObject  = TryUnwrapConvertToObject(left, out var leftOperand);
            var isRightConvertToObject = TryUnwrapConvertToObject(right, out var rightOperand);

            if (isLeftConvertToObject && isRightConvertToObject)
            {
                left  = leftOperand;
                right = rightOperand;
            }
            else if (isLeftConvertToObject && right.IsNullConstantExpression())
            {
                left = leftOperand;
            }
            else if (isRightConvertToObject && left.IsNullConstantExpression())
            {
                right = rightOperand;
            }

            var visitedLeft  = Visit(left);
            var visitedRight = Visit(right);

            if ((binaryExpression.NodeType == ExpressionType.Equal ||
                 binaryExpression.NodeType == ExpressionType.NotEqual)
                // Visited expression could be null, We need to pass MemberInitExpression
                && TryRewriteEntityEquality(
                    binaryExpression.NodeType, visitedLeft ?? left, visitedRight ?? right, equalsMethod: false, out var result))
            {
                return(result);
            }

            if (binaryExpression.Method == _concatMethodInfo)
            {
                return(null);
            }

            var uncheckedNodeTypeVariant = binaryExpression.NodeType switch
            {
                ExpressionType.AddChecked => ExpressionType.Add,
                ExpressionType.SubtractChecked => ExpressionType.Subtract,
                ExpressionType.MultiplyChecked => ExpressionType.Multiply,
                _ => binaryExpression.NodeType
            };

            return(TranslationFailed(binaryExpression.Left, visitedLeft, out var sqlLeft) ||
                   TranslationFailed(binaryExpression.Right, visitedRight, out var sqlRight)
                    ? null
                    : _sqlExpressionFactory.MakeBinary(
                       uncheckedNodeTypeVariant,
                       sqlLeft,
                       sqlRight,
                       null));
コード例 #12
0
        private Expression VisitSqlUnaryExpression(SqlUnaryExpression sqlUnaryExpression)
        {
            // !(true) -> false
            // !(false) -> true
            if (sqlUnaryExpression.OperatorType == ExpressionType.Not &&
                sqlUnaryExpression.Operand is SqlConstantExpression innerConstantBool &&
                innerConstantBool.Value is bool value)
            {
                return(value
                    ? _sqlExpressionFactory.Constant(false, sqlUnaryExpression.TypeMapping)
                    : _sqlExpressionFactory.Constant(true, sqlUnaryExpression.TypeMapping));
            }

            // NULL IS NULL -> true
            // non_nullablee_constant IS NULL -> false
            if (sqlUnaryExpression.OperatorType == ExpressionType.Equal &&
                sqlUnaryExpression.Operand is SqlConstantExpression innerConstantNull1)
            {
                return(_sqlExpressionFactory.Constant(innerConstantNull1.Value == null, sqlUnaryExpression.TypeMapping));
            }

            // NULL IS NOT NULL -> false
            // non_nullablee_constant IS NOT NULL -> true
            if (sqlUnaryExpression.OperatorType == ExpressionType.NotEqual &&
                sqlUnaryExpression.Operand is SqlConstantExpression innerConstantNull2)
            {
                return(_sqlExpressionFactory.Constant(innerConstantNull2.Value != null, sqlUnaryExpression.TypeMapping));
            }

            if (sqlUnaryExpression.Operand is SqlUnaryExpression innerUnary)
            {
                if (sqlUnaryExpression.OperatorType == ExpressionType.Not)
                {
                    // !(!a) -> a
                    if (innerUnary.OperatorType == ExpressionType.Not)
                    {
                        return(Visit(innerUnary.Operand));
                    }

                    if (innerUnary.OperatorType == ExpressionType.Equal)
                    {
                        //!(a IS NULL) -> a IS NOT NULL
                        return(Visit(_sqlExpressionFactory.IsNotNull(innerUnary.Operand)));
                    }

                    //!(a IS NOT NULL) -> a IS NULL
                    if (innerUnary.OperatorType == ExpressionType.NotEqual)
                    {
                        return(Visit(_sqlExpressionFactory.IsNull(innerUnary.Operand)));
                    }
                }

                // (!a) IS NULL <==> a IS NULL
                if (sqlUnaryExpression.OperatorType == ExpressionType.Equal &&
                    innerUnary.OperatorType == ExpressionType.Not)
                {
                    return(Visit(_sqlExpressionFactory.IsNull(innerUnary.Operand)));
                }

                // (!a) IS NOT NULL <==> a IS NOT NULL
                if (sqlUnaryExpression.OperatorType == ExpressionType.NotEqual &&
                    innerUnary.OperatorType == ExpressionType.Not)
                {
                    return(Visit(_sqlExpressionFactory.IsNotNull(innerUnary.Operand)));
                }
            }

            if (sqlUnaryExpression.Operand is SqlBinaryExpression innerBinary)
            {
                // De Morgan's
                if (innerBinary.OperatorType == ExpressionType.AndAlso ||
                    innerBinary.OperatorType == ExpressionType.OrElse)
                {
                    var newLeft  = (SqlExpression)Visit(_sqlExpressionFactory.Not(innerBinary.Left));
                    var newRight = (SqlExpression)Visit(_sqlExpressionFactory.Not(innerBinary.Right));

                    return(innerBinary.OperatorType == ExpressionType.AndAlso
                        ? _sqlExpressionFactory.OrElse(newLeft, newRight)
                        : _sqlExpressionFactory.AndAlso(newLeft, newRight));
                }

                // note that 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 (TryNegate(innerBinary.OperatorType, out var negated))
                {
                    return(Visit(
                               _sqlExpressionFactory.MakeBinary(
                                   negated,
                                   innerBinary.Left,
                                   innerBinary.Right,
                                   innerBinary.TypeMapping)));
                }
            }

            var newOperand = (SqlExpression)Visit(sqlUnaryExpression.Operand);

            return(sqlUnaryExpression.Update(newOperand));
        }
コード例 #13
0
        private SqlExpression SimplifyBinaryExpression(
            ExpressionType operatorType,
            SqlExpression left,
            SqlExpression right,
            RelationalTypeMapping typeMapping)
        {
            switch (operatorType)
            {
            case ExpressionType.AndAlso:
            case ExpressionType.OrElse:
                var leftUnary  = left as SqlUnaryExpression;
                var rightUnary = right as SqlUnaryExpression;
                if (leftUnary != null &&
                    rightUnary != null &&
                    (leftUnary.OperatorType == ExpressionType.Equal || leftUnary.OperatorType == ExpressionType.NotEqual) &&
                    (rightUnary.OperatorType == ExpressionType.Equal || rightUnary.OperatorType == ExpressionType.NotEqual) &&
                    leftUnary.Operand.Equals(rightUnary.Operand))
                {
                    // a is null || a is null -> a is null
                    // a is not null || a is not null -> a is not null
                    // a is null && a is null -> a is null
                    // a is not null && a is not null -> a is not null
                    // a is null || a is not null -> true
                    // a is null && a is not null -> false
                    return(leftUnary.OperatorType == rightUnary.OperatorType
                            ? (SqlExpression)leftUnary
                            : _sqlExpressionFactory.Constant(operatorType == ExpressionType.OrElse, typeMapping));
                }

                return(SimplifyLogicalSqlBinaryExpression(
                           operatorType,
                           left,
                           right,
                           typeMapping));

            case ExpressionType.Equal:
            case ExpressionType.NotEqual:
                var leftConstant      = left as SqlConstantExpression;
                var rightConstant     = right as SqlConstantExpression;
                var leftNullConstant  = leftConstant != null && leftConstant.Value == null;
                var rightNullConstant = rightConstant != null && rightConstant.Value == null;
                if (leftNullConstant || rightNullConstant)
                {
                    return(SimplifyNullComparisonExpression(
                               operatorType,
                               left,
                               right,
                               leftNullConstant,
                               rightNullConstant,
                               typeMapping));
                }

                var leftBoolValue  = left.Type == typeof(bool) ? (bool?)leftConstant?.Value : null;
                var rightBoolValue = right.Type == typeof(bool) ? (bool?)rightConstant?.Value : null;
                if (leftBoolValue != null || rightBoolValue != null)
                {
                    return(SimplifyBoolConstantComparisonExpression(
                               operatorType,
                               left,
                               right,
                               leftBoolValue,
                               rightBoolValue,
                               typeMapping));
                }

                // only works when a is not nullable
                // a == a -> true
                // a != a -> false
                if ((left is LikeExpression ||
                     left is ColumnExpression columnExpression && !columnExpression.IsNullable) &&
                    left.Equals(right))
                {
                    return(_sqlExpressionFactory.Constant(operatorType == ExpressionType.Equal, typeMapping));
                }

                break;
            }

            return(_sqlExpressionFactory.MakeBinary(operatorType, left, right, typeMapping) !);
        }