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)); }
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); }
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); }
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)); }