public virtual SqlExpression Translate(SqlExpression instance, MethodInfo method, IReadOnlyList <SqlExpression> arguments)
 {
     if (s_defaultMethods.TryGetValue(method, out var defaultValue))
     {
         return(_sqlExpressionFactory.Coalesce(instance, _sqlExpressionFactory.Constant(defaultValue), instance.TypeMapping));
     }
     if (s_defaultMethodsWithArgument.Contains(method))
     {
         return(_sqlExpressionFactory.Coalesce(instance, arguments[0], instance.TypeMapping));
     }
     return(null);
 }
        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));
        }
Пример #3
0
        public virtual SqlExpression Translate(SqlExpression instance, MethodInfo method, IReadOnlyList <SqlExpression> arguments)
        {
            if (method.Name == nameof(Nullable <int> .GetValueOrDefault) &&
                method.ReturnType.IsNumeric())
            {
                return(_sqlExpressionFactory.Coalesce(
                           instance,
                           arguments.Count == 0
                        ? GetDefaultConstant(method.ReturnType)
                        : arguments[0],
                           instance.TypeMapping));
            }

            return(null);
        }
Пример #4
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));
        }
        /// <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>
        public virtual SqlExpression?Translate(
            SqlExpression?instance,
            MethodInfo method,
            IReadOnlyList <SqlExpression> arguments,
            IDiagnosticsLogger <DbLoggerCategory.Query> logger)
        {
            if (method.Name == nameof(Nullable <int> .GetValueOrDefault) &&
                instance != null &&
                method.ReturnType.IsNumeric())
            {
                return(_sqlExpressionFactory.Coalesce(
                           instance,
                           arguments.Count == 0
                        ? new SqlConstantExpression(method.ReturnType.GetDefaultValueConstant(), null)
                        : arguments[0],
                           instance.TypeMapping));
            }

            return(null);
        }
Пример #6
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));
        }