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