From 8b8af811e314e60d9cf97c3d7656d0275ac3253d Mon Sep 17 00:00:00 2001 From: Shay Rojansky Date: Sun, 4 Jun 2023 12:15:14 +0200 Subject: [PATCH] Better management of negated expressions Closes #31027 --- .../Internal/CosmosContainsTranslator.cs | 4 +- ...ressionValuesExpandingExpressionVisitor.cs | 3 +- .../CosmosSqlTranslatingExpressionVisitor.cs | 3 +- .../Query/Internal/ISqlExpressionFactory.cs | 2 +- .../Query/Internal/InExpression.cs | 26 +----- .../Query/Internal/QuerySqlGenerator.cs | 26 +++++- .../Query/Internal/SqlExpressionFactory.cs | 7 +- .../Query/ISqlExpressionFactory.cs | 9 +- .../Query/Internal/ContainsTranslator.cs | 4 +- ...lExpressionSimplifyingExpressionVisitor.cs | 29 +++++-- .../Query/QuerySqlGenerator.cs | 84 +++++++++++++++---- ...yableMethodTranslatingExpressionVisitor.cs | 8 +- ...lationalSqlTranslatingExpressionVisitor.cs | 18 ++-- .../Query/SqlExpressionFactory.cs | 18 ++-- .../Query/SqlExpressions/ExistsExpression.cs | 27 +----- .../Query/SqlExpressions/InExpression.cs | 29 ++----- .../Query/SqlExpressions/SelectExpression.cs | 3 +- .../Query/SqlNullabilityProcessor.cs | 63 ++++---------- .../Query/Internal/SqliteQuerySqlGenerator.cs | 59 ++++++++++--- .../Internal/SqliteSqlExpressionFactory.cs | 12 +-- .../Internal/SqliteSqlNullabilityProcessor.cs | 23 ----- .../SqlExpressions/Internal/GlobExpression.cs | 33 +------- .../Internal/RegexpExpression.cs | 33 +------- ...thwindAggregateOperatorsQueryCosmosTest.cs | 14 ++-- .../Query/NorthwindWhereQueryCosmosTest.cs | 2 +- .../Query/UdfDbFunctionTestBase.cs | 3 - .../ApiConsistencyTestBase.cs | 2 +- .../Query/FunkyDataQuerySqlServerTest.cs | 8 +- .../ManyToManyNoTrackingQuerySqlServerTest.cs | 2 +- .../Query/ManyToManyQuerySqlServerTest.cs | 2 +- .../NorthwindFunctionsQuerySqlServerTest.cs | 4 +- ...orthwindMiscellaneousQuerySqlServerTest.cs | 14 ++-- .../Query/OperatorsQuerySqlServerTest.cs | 2 +- ...CManyToManyNoTrackingQuerySqlServerTest.cs | 4 +- .../Query/TPCManyToManyQuerySqlServerTest.cs | 4 +- ...TManyToManyNoTrackingQuerySqlServerTest.cs | 4 +- .../Query/TPTManyToManyQuerySqlServerTest.cs | 4 +- .../TemporalManyToManyQuerySqlServerTest.cs | 2 +- .../Query/OperatorsQuerySqliteTest.cs | 2 +- 39 files changed, 265 insertions(+), 331 deletions(-) diff --git a/src/EFCore.Cosmos/Query/Internal/CosmosContainsTranslator.cs b/src/EFCore.Cosmos/Query/Internal/CosmosContainsTranslator.cs index 2f247a92847..83269423772 100644 --- a/src/EFCore.Cosmos/Query/Internal/CosmosContainsTranslator.cs +++ b/src/EFCore.Cosmos/Query/Internal/CosmosContainsTranslator.cs @@ -40,7 +40,7 @@ public CosmosContainsTranslator(ISqlExpressionFactory sqlExpressionFactory) && method.GetGenericMethodDefinition().Equals(EnumerableMethods.Contains) && ValidateValues(arguments[0])) { - return _sqlExpressionFactory.In(arguments[1], arguments[0], false); + return _sqlExpressionFactory.In(arguments[1], arguments[0]); } if (arguments.Count == 1 @@ -48,7 +48,7 @@ public CosmosContainsTranslator(ISqlExpressionFactory sqlExpressionFactory) && instance != null && ValidateValues(instance)) { - return _sqlExpressionFactory.In(arguments[0], instance, false); + return _sqlExpressionFactory.In(arguments[0], instance); } return null; diff --git a/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.InExpressionValuesExpandingExpressionVisitor.cs b/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.InExpressionValuesExpandingExpressionVisitor.cs index 2fbe08418de..2e74cc6317b 100644 --- a/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.InExpressionValuesExpandingExpressionVisitor.cs +++ b/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.InExpressionValuesExpandingExpressionVisitor.cs @@ -70,8 +70,7 @@ public override Expression Visit(Expression expression) var updatedInExpression = inValues.Count > 0 ? _sqlExpressionFactory.In( (SqlExpression)Visit(inExpression.Item), - _sqlExpressionFactory.Constant(inValues, typeMapping), - inExpression.IsNegated) + _sqlExpressionFactory.Constant(inValues, typeMapping)) : null; var nullCheckExpression = hasNullValue diff --git a/src/EFCore.Cosmos/Query/Internal/CosmosSqlTranslatingExpressionVisitor.cs b/src/EFCore.Cosmos/Query/Internal/CosmosSqlTranslatingExpressionVisitor.cs index 58017112e34..e52749044e0 100644 --- a/src/EFCore.Cosmos/Query/Internal/CosmosSqlTranslatingExpressionVisitor.cs +++ b/src/EFCore.Cosmos/Query/Internal/CosmosSqlTranslatingExpressionVisitor.cs @@ -704,8 +704,7 @@ protected override Expression VisitTypeBinary(TypeBinaryExpression typeBinaryExp _sqlExpressionFactory.Constant(concreteEntityTypes[0].GetDiscriminatorValue())) : _sqlExpressionFactory.In( discriminatorColumn, - _sqlExpressionFactory.Constant(concreteEntityTypes.Select(et => et.GetDiscriminatorValue()).ToList()), - negated: false); + _sqlExpressionFactory.Constant(concreteEntityTypes.Select(et => et.GetDiscriminatorValue()).ToList())); } } diff --git a/src/EFCore.Cosmos/Query/Internal/ISqlExpressionFactory.cs b/src/EFCore.Cosmos/Query/Internal/ISqlExpressionFactory.cs index d78b711672c..01c619fa612 100644 --- a/src/EFCore.Cosmos/Query/Internal/ISqlExpressionFactory.cs +++ b/src/EFCore.Cosmos/Query/Internal/ISqlExpressionFactory.cs @@ -256,7 +256,7 @@ SqlConditionalExpression Condition( /// 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. /// - InExpression In(SqlExpression item, SqlExpression values, bool negated); + InExpression In(SqlExpression item, SqlExpression values); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to diff --git a/src/EFCore.Cosmos/Query/Internal/InExpression.cs b/src/EFCore.Cosmos/Query/Internal/InExpression.cs index a7fc38ce7ea..4a3339c950b 100644 --- a/src/EFCore.Cosmos/Query/Internal/InExpression.cs +++ b/src/EFCore.Cosmos/Query/Internal/InExpression.cs @@ -19,13 +19,11 @@ public class InExpression : SqlExpression /// public InExpression( SqlExpression item, - bool negated, SqlExpression values, CoreTypeMapping typeMapping) : base(typeof(bool), typeMapping) { Item = item; - IsNegated = negated; Values = values; } @@ -37,14 +35,6 @@ public InExpression( /// public virtual SqlExpression Item { get; } - /// - /// 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. - /// - public virtual bool IsNegated { get; } - /// /// 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 @@ -67,15 +57,6 @@ protected override Expression VisitChildren(ExpressionVisitor visitor) return Update(newItem, values); } - /// - /// 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. - /// - public virtual InExpression Negate() - => new(Item, !IsNegated, Values, TypeMapping!); - /// /// 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 @@ -84,7 +65,7 @@ public virtual InExpression Negate() /// public virtual InExpression Update(SqlExpression item, SqlExpression values) => item != Item || values != Values - ? new InExpression(item, IsNegated, values, TypeMapping!) + ? new InExpression(item, values, TypeMapping!) : this; /// @@ -96,7 +77,7 @@ public virtual InExpression Update(SqlExpression item, SqlExpression values) protected override void Print(ExpressionPrinter expressionPrinter) { expressionPrinter.Visit(Item); - expressionPrinter.Append(IsNegated ? " NOT IN " : " IN "); + expressionPrinter.Append(" IN "); expressionPrinter.Append("("); expressionPrinter.Visit(Values); expressionPrinter.Append(")"); @@ -117,7 +98,6 @@ public override bool Equals(object? obj) private bool Equals(InExpression inExpression) => base.Equals(inExpression) && Item.Equals(inExpression.Item) - && IsNegated.Equals(inExpression.IsNegated) && Values.Equals(inExpression.Values); /// @@ -127,5 +107,5 @@ private bool Equals(InExpression inExpression) /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public override int GetHashCode() - => HashCode.Combine(base.GetHashCode(), Item, IsNegated, Values); + => HashCode.Combine(base.GetHashCode(), Item, Values); } diff --git a/src/EFCore.Cosmos/Query/Internal/QuerySqlGenerator.cs b/src/EFCore.Cosmos/Query/Internal/QuerySqlGenerator.cs index 4e80f07ea66..4d1bc4207cb 100644 --- a/src/EFCore.Cosmos/Query/Internal/QuerySqlGenerator.cs +++ b/src/EFCore.Cosmos/Query/Internal/QuerySqlGenerator.cs @@ -387,6 +387,13 @@ protected override Expression VisitSqlUnary(SqlUnaryExpression sqlUnaryExpressio if (sqlUnaryExpression.OperatorType == ExpressionType.Not && sqlUnaryExpression.Operand.Type == typeof(bool)) { + if (sqlUnaryExpression.Operand is InExpression inExpression) + { + GenerateIn(inExpression, negated: true); + + return sqlUnaryExpression; + } + op = "NOT"; } @@ -506,18 +513,29 @@ protected override Expression VisitSqlParameter(SqlParameterExpression sqlParame /// 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. /// - protected override Expression VisitIn(InExpression inExpression) + protected sealed override Expression VisitIn(InExpression inExpression) + { + GenerateIn(inExpression, negated: false); + + return inExpression; + } + + /// + /// 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. + /// + protected virtual void GenerateIn(InExpression inExpression, bool negated) { Visit(inExpression.Item); - _sqlBuilder.Append(inExpression.IsNegated ? " NOT IN " : " IN "); + _sqlBuilder.Append(negated ? " NOT IN " : " IN "); _sqlBuilder.Append('('); var valuesConstant = (SqlConstantExpression)inExpression.Values; var valuesList = ((IEnumerable)valuesConstant.Value) .Select(v => new SqlConstantExpression(Expression.Constant(v), valuesConstant.TypeMapping)).ToList(); GenerateList(valuesList, e => Visit(e)); _sqlBuilder.Append(')'); - - return inExpression; } /// diff --git a/src/EFCore.Cosmos/Query/Internal/SqlExpressionFactory.cs b/src/EFCore.Cosmos/Query/Internal/SqlExpressionFactory.cs index 87645bc90d7..a571a7914b7 100644 --- a/src/EFCore.Cosmos/Query/Internal/SqlExpressionFactory.cs +++ b/src/EFCore.Cosmos/Query/Internal/SqlExpressionFactory.cs @@ -473,14 +473,14 @@ public virtual SqlConditionalExpression Condition(SqlExpression test, SqlExpress /// 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. /// - public virtual InExpression In(SqlExpression item, SqlExpression values, bool negated) + public virtual InExpression In(SqlExpression item, SqlExpression values) { var typeMapping = item.TypeMapping ?? _typeMappingSource.FindMapping(item.Type, _model); item = ApplyTypeMapping(item, typeMapping); values = ApplyTypeMapping(values, typeMapping); - return new InExpression(item, negated, values, _boolTypeMapping); + return new InExpression(item, values, _boolTypeMapping); } /// @@ -539,8 +539,7 @@ private void AddDiscriminator(SelectExpression selectExpression, IEntityType ent selectExpression.ApplyPredicate( In( - (SqlExpression)discriminatorColumn, Constant(concreteEntityTypes.Select(et => et.GetDiscriminatorValue()).ToList()), - negated: false)); + (SqlExpression)discriminatorColumn, Constant(concreteEntityTypes.Select(et => et.GetDiscriminatorValue()).ToList()))); } } } diff --git a/src/EFCore.Relational/Query/ISqlExpressionFactory.cs b/src/EFCore.Relational/Query/ISqlExpressionFactory.cs index 8780256848d..3fa2be11a49 100644 --- a/src/EFCore.Relational/Query/ISqlExpressionFactory.cs +++ b/src/EFCore.Relational/Query/ISqlExpressionFactory.cs @@ -399,27 +399,24 @@ SqlFunctionExpression NiladicFunction( /// Creates a new which represents an EXISTS operation in a SQL tree. /// /// A subquery to check existence of. - /// A value indicating if the existence check is negated. /// An expression representing an EXISTS operation in a SQL tree. - ExistsExpression Exists(SelectExpression subquery, bool negated); + ExistsExpression Exists(SelectExpression subquery); /// /// Creates a new which represents an IN operation in a SQL tree. /// /// An item to look into values. /// A list of values in which item is searched. - /// A value indicating if the item should be present in the values or absent. /// An expression representing an IN operation in a SQL tree. - InExpression In(SqlExpression item, SqlExpression values, bool negated); + InExpression In(SqlExpression item, SqlExpression values); /// /// Creates a new which represents an IN operation in a SQL tree. /// /// An item to look into values. /// A subquery in which item is searched. - /// A value indicating if the item should be present in the values or absent. /// An expression representing an IN operation in a SQL tree. - InExpression In(SqlExpression item, SelectExpression subquery, bool negated); + InExpression In(SqlExpression item, SelectExpression subquery); /// /// Creates a new which represents a LIKE in a SQL tree. diff --git a/src/EFCore.Relational/Query/Internal/ContainsTranslator.cs b/src/EFCore.Relational/Query/Internal/ContainsTranslator.cs index 0e523bf70a9..e27e6c9e536 100644 --- a/src/EFCore.Relational/Query/Internal/ContainsTranslator.cs +++ b/src/EFCore.Relational/Query/Internal/ContainsTranslator.cs @@ -42,7 +42,7 @@ public ContainsTranslator(ISqlExpressionFactory sqlExpressionFactory) && method.GetGenericMethodDefinition().Equals(EnumerableMethods.Contains) && ValidateValues(arguments[0])) { - return _sqlExpressionFactory.In(RemoveObjectConvert(arguments[1]), arguments[0], negated: false); + return _sqlExpressionFactory.In(RemoveObjectConvert(arguments[1]), arguments[0]); } if (arguments.Count == 1 @@ -50,7 +50,7 @@ public ContainsTranslator(ISqlExpressionFactory sqlExpressionFactory) && instance != null && ValidateValues(instance)) { - return _sqlExpressionFactory.In(RemoveObjectConvert(arguments[0]), instance, negated: false); + return _sqlExpressionFactory.In(RemoveObjectConvert(arguments[0]), instance); } return null; diff --git a/src/EFCore.Relational/Query/Internal/SqlExpressionSimplifyingExpressionVisitor.cs b/src/EFCore.Relational/Query/Internal/SqlExpressionSimplifyingExpressionVisitor.cs index 35f566a35e9..62585199c34 100644 --- a/src/EFCore.Relational/Query/Internal/SqlExpressionSimplifyingExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/Internal/SqlExpressionSimplifyingExpressionVisitor.cs @@ -307,10 +307,16 @@ or ExpressionType.LessThan break; } - return _sqlExpressionFactory.In( + var inExpression = _sqlExpressionFactory.In( leftCandidateInfo.ColumnExpression, - _sqlExpressionFactory.Constant(resultArray, leftCandidateInfo.TypeMapping), - leftCandidateInfo.OperationType == ExpressionType.NotEqual); + _sqlExpressionFactory.Constant(resultArray, leftCandidateInfo.TypeMapping)); + + return leftCandidateInfo.OperationType switch + { + ExpressionType.Equal => inExpression, + ExpressionType.NotEqual => _sqlExpressionFactory.Not(inExpression), + _ => throw new InvalidOperationException("IMPOSSIBLE") + }; } if (leftConstantIsEnumerable && rightConstantIsEnumerable) @@ -321,10 +327,16 @@ or ExpressionType.LessThan (IEnumerable)leftCandidateInfo.ConstantValue, (IEnumerable)rightCandidateInfo.ConstantValue); - return _sqlExpressionFactory.In( + var inExpression = _sqlExpressionFactory.In( leftCandidateInfo.ColumnExpression, - _sqlExpressionFactory.Constant(resultArray, leftCandidateInfo.TypeMapping), - leftCandidateInfo.OperationType == ExpressionType.NotEqual); + _sqlExpressionFactory.Constant(resultArray, leftCandidateInfo.TypeMapping)); + + return leftCandidateInfo.OperationType switch + { + ExpressionType.Equal => inExpression, + ExpressionType.NotEqual => _sqlExpressionFactory.Not(inExpression), + _ => throw new InvalidOperationException("IMPOSSIBLE") + }; } } } @@ -424,10 +436,9 @@ private static bool TryGetInExpressionCandidateInfo( else if (sqlExpression is InExpression { Item: ColumnExpression column, Subquery: null, Values: SqlConstantExpression valuesConstant - } inExpression) + }) { - candidateInfo = (column, valuesConstant.Value!, valuesConstant.TypeMapping!, - inExpression.IsNegated ? ExpressionType.NotEqual : ExpressionType.Equal); + candidateInfo = (column, valuesConstant.Value!, valuesConstant.TypeMapping!, ExpressionType.Equal); return true; } diff --git a/src/EFCore.Relational/Query/QuerySqlGenerator.cs b/src/EFCore.Relational/Query/QuerySqlGenerator.cs index 4be62a43408..6b842759f67 100644 --- a/src/EFCore.Relational/Query/QuerySqlGenerator.cs +++ b/src/EFCore.Relational/Query/QuerySqlGenerator.cs @@ -716,9 +716,27 @@ protected override Expression VisitOrdering(OrderingExpression orderingExpressio } /// - protected override Expression VisitLike(LikeExpression likeExpression) + protected sealed override Expression VisitLike(LikeExpression likeExpression) + { + GenerateLike(likeExpression, negated: false); + + return likeExpression; + } + + /// + /// Generates SQL for the LIKE expression. + /// + /// The expression to visit. + /// Whether the given is negated. + protected virtual void GenerateLike(LikeExpression likeExpression, bool negated) { Visit(likeExpression.Match); + + if (negated) + { + _relationalCommandBuilder.Append(" NOT"); + } + _relationalCommandBuilder.Append(" LIKE "); Visit(likeExpression.Pattern); @@ -727,8 +745,6 @@ protected override Expression VisitLike(LikeExpression likeExpression) _relationalCommandBuilder.Append(" ESCAPE "); Visit(likeExpression.EscapeChar); } - - return likeExpression; } /// @@ -821,9 +837,27 @@ protected override Expression VisitSqlUnary(SqlUnaryExpression sqlUnaryExpressio case ExpressionType.Not when sqlUnaryExpression.Type == typeof(bool): { - _relationalCommandBuilder.Append("NOT ("); - Visit(sqlUnaryExpression.Operand); - _relationalCommandBuilder.Append(")"); + switch (sqlUnaryExpression.Operand) + { + case InExpression inExpression: + GenerateIn(inExpression, negated: true); + break; + + case ExistsExpression existsExpression: + GenerateExists(existsExpression, negated: true); + break; + + case LikeExpression likeExpression: + GenerateLike(likeExpression, negated: true); + break; + + default: + _relationalCommandBuilder.Append("NOT ("); + Visit(sqlUnaryExpression.Operand); + _relationalCommandBuilder.Append(")"); + break; + } + break; } @@ -905,9 +939,21 @@ protected override Expression VisitSqlUnary(SqlUnaryExpression sqlUnaryExpressio } /// - protected override Expression VisitExists(ExistsExpression existsExpression) + protected sealed override Expression VisitExists(ExistsExpression existsExpression) { - if (existsExpression.IsNegated) + GenerateExists(existsExpression, negated: false); + + return existsExpression; + } + + /// + /// Generates SQL for the EXISTS expression. + /// + /// The expression to visit. + /// Whether the given is negated. + protected virtual void GenerateExists(ExistsExpression existsExpression, bool negated) + { + if (negated) { _relationalCommandBuilder.Append("NOT "); } @@ -920,17 +966,27 @@ protected override Expression VisitExists(ExistsExpression existsExpression) } _relationalCommandBuilder.Append(")"); - - return existsExpression; } /// - protected override Expression VisitIn(InExpression inExpression) + protected sealed override Expression VisitIn(InExpression inExpression) + { + GenerateIn(inExpression, negated: false); + + return inExpression; + } + + /// + /// Generates SQL for the IN expression. + /// + /// The expression to visit. + /// Whether the given is negated. + protected virtual void GenerateIn(InExpression inExpression, bool negated) { if (inExpression.Values != null) { Visit(inExpression.Item); - _relationalCommandBuilder.Append(inExpression.IsNegated ? " NOT IN " : " IN "); + _relationalCommandBuilder.Append(negated ? " NOT IN " : " IN "); _relationalCommandBuilder.Append("("); var valuesConstant = (SqlConstantExpression)inExpression.Values; var valuesList = ((IEnumerable)valuesConstant.Value!) @@ -941,7 +997,7 @@ protected override Expression VisitIn(InExpression inExpression) else { Visit(inExpression.Item); - _relationalCommandBuilder.Append(inExpression.IsNegated ? " NOT IN " : " IN "); + _relationalCommandBuilder.Append(negated ? " NOT IN " : " IN "); _relationalCommandBuilder.AppendLine("("); using (_relationalCommandBuilder.Indent()) @@ -951,8 +1007,6 @@ protected override Expression VisitIn(InExpression inExpression) _relationalCommandBuilder.AppendLine().Append(")"); } - - return inExpression; } /// diff --git a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs index fec3abd6c8e..fdcd8334896 100644 --- a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs @@ -427,7 +427,7 @@ private static ShapedQueryExpression CreateShapedQueryExpression(IEntityType ent subquery.ClearOrdering(); } - translation = _sqlExpressionFactory.Exists(subquery, true); + translation = _sqlExpressionFactory.Not(_sqlExpressionFactory.Exists(subquery)); subquery = _sqlExpressionFactory.Select(translation); return source.Update( @@ -458,7 +458,7 @@ private static ShapedQueryExpression CreateShapedQueryExpression(IEntityType ent subquery.ClearOrdering(); } - var translation = _sqlExpressionFactory.Exists(subquery, false); + var translation = _sqlExpressionFactory.Exists(subquery); var selectExpression = _sqlExpressionFactory.Select(translation); return source.Update( @@ -547,7 +547,7 @@ private static ShapedQueryExpression CreateShapedQueryExpression(IEntityType ent if (values is not null) { - var inExpression = _sqlExpressionFactory.In(translatedItem, _sqlExpressionFactory.Constant(values), negated: false); + var inExpression = _sqlExpressionFactory.In(translatedItem, _sqlExpressionFactory.Constant(values)); return source.Update(_sqlExpressionFactory.Select(inExpression), source.ShaperExpression); } } @@ -564,7 +564,7 @@ private static ShapedQueryExpression CreateShapedQueryExpression(IEntityType ent subquery.ReplaceProjection(new List { projection }); subquery.ApplyProjection(); - var translation = _sqlExpressionFactory.In(translatedItem, subquery, false); + var translation = _sqlExpressionFactory.In(translatedItem, subquery); subquery = _sqlExpressionFactory.Select(translation); return source.Update( diff --git a/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs b/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs index 3d50be65513..8749b8d3585 100644 --- a/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs @@ -485,7 +485,7 @@ Expression ProcessGetType(EntityReferenceExpression entityReferenceExpression, T subSelectExpression.ClearOrdering(); } - return _sqlExpressionFactory.Exists(subSelectExpression, false); + return _sqlExpressionFactory.Exists(subSelectExpression); } if (entityReferenceExpression.ParameterEntity != null) @@ -1064,7 +1064,7 @@ protected override Expression VisitTypeBinary(TypeBinaryExpression typeBinaryExp subSelectExpression.ClearOrdering(); } - return _sqlExpressionFactory.Exists(subSelectExpression, false); + return _sqlExpressionFactory.Exists(subSelectExpression); } if (entityReferenceExpression.ParameterEntity != null) @@ -1095,8 +1095,7 @@ SqlExpression GeneratePredicateTpt(EntityProjectionExpression entityProjectionEx _sqlExpressionFactory.Constant(discriminatorValues[0])) : _sqlExpressionFactory.In( entityProjectionExpression.DiscriminatorExpression!, - _sqlExpressionFactory.Constant(discriminatorValues), - negated: false); + _sqlExpressionFactory.Constant(discriminatorValues)); } } else @@ -1116,8 +1115,7 @@ SqlExpression GeneratePredicateTpt(EntityProjectionExpression entityProjectionEx : _sqlExpressionFactory.In( discriminatorColumn, _sqlExpressionFactory.Constant( - concreteEntityTypes.Select(et => et.GetDiscriminatorValue()).ToList()), - negated: false); + concreteEntityTypes.Select(et => et.GetDiscriminatorValue()).ToList())); } } else @@ -1151,13 +1149,7 @@ protected override Expression VisitUnary(UnaryExpression unaryExpression) switch (unaryExpression.NodeType) { case ExpressionType.Not: - return sqlOperand switch - { - ExistsExpression e => e.Negate(), - InExpression e => e.Negate(), - - _ => _sqlExpressionFactory.Not(sqlOperand!) - }; + return _sqlExpressionFactory.Not(sqlOperand!); case ExpressionType.Negate: case ExpressionType.NegateChecked: diff --git a/src/EFCore.Relational/Query/SqlExpressionFactory.cs b/src/EFCore.Relational/Query/SqlExpressionFactory.cs index 77898d2898f..1f81e7d826b 100644 --- a/src/EFCore.Relational/Query/SqlExpressionFactory.cs +++ b/src/EFCore.Relational/Query/SqlExpressionFactory.cs @@ -254,12 +254,12 @@ private SqlExpression ApplyTypeMappingOnIn(InExpression inExpression) var values = ApplyTypeMapping(inExpression.Values, itemTypeMapping); return item != inExpression.Item || values != inExpression.Values || inExpression.TypeMapping != _boolTypeMapping - ? new InExpression(item, values, inExpression.IsNegated, _boolTypeMapping) + ? new InExpression(item, values, _boolTypeMapping) : inExpression; } return item != inExpression.Item || inExpression.TypeMapping != _boolTypeMapping - ? new InExpression(item, inExpression.Subquery!, inExpression.IsNegated, _boolTypeMapping) + ? new InExpression(item, inExpression.Subquery!, _boolTypeMapping) : inExpression; } @@ -582,22 +582,22 @@ public virtual SqlFunctionExpression NiladicFunction( ApplyDefaultTypeMapping(instance), name, nullable, instancePropagatesNullability, returnType, typeMapping); /// - public virtual ExistsExpression Exists(SelectExpression subquery, bool negated) - => new(subquery, negated, _boolTypeMapping); + public virtual ExistsExpression Exists(SelectExpression subquery) + => new(subquery, _boolTypeMapping); /// - public virtual InExpression In(SqlExpression item, SqlExpression values, bool negated) + public virtual InExpression In(SqlExpression item, SqlExpression values) { var typeMapping = item.TypeMapping ?? _typeMappingSource.FindMapping(item.Type, Dependencies.Model); item = ApplyTypeMapping(item, typeMapping); values = ApplyTypeMapping(values, typeMapping); - return new InExpression(item, values, negated, _boolTypeMapping); + return new InExpression(item, values, _boolTypeMapping); } /// - public virtual InExpression In(SqlExpression item, SelectExpression subquery, bool negated) + public virtual InExpression In(SqlExpression item, SelectExpression subquery) { var sqlExpression = subquery.Projection.Single().Expression; var subqueryTypeMapping = sqlExpression.TypeMapping; @@ -607,7 +607,7 @@ public virtual InExpression In(SqlExpression item, SelectExpression subquery, bo item = subqueryTypeMapping is null ? ApplyDefaultTypeMapping(item) : ApplyTypeMapping(item, subqueryTypeMapping); } - return new InExpression(item, subquery, negated, _boolTypeMapping); + return new InExpression(item, subquery, _boolTypeMapping); } /// @@ -671,7 +671,7 @@ private void AddConditions(SelectExpression selectExpression, IEntityType entity var concreteEntityTypes = entityType.GetConcreteDerivedTypesInclusive().ToList(); var predicate = concreteEntityTypes.Count == 1 ? (SqlExpression)Equal(discriminatorColumn, Constant(concreteEntityTypes[0].GetDiscriminatorValue())) - : In(discriminatorColumn, Constant(concreteEntityTypes.Select(et => et.GetDiscriminatorValue()).ToList()), negated: false); + : In(discriminatorColumn, Constant(concreteEntityTypes.Select(et => et.GetDiscriminatorValue()).ToList())); selectExpression.ApplyPredicate(predicate); diff --git a/src/EFCore.Relational/Query/SqlExpressions/ExistsExpression.cs b/src/EFCore.Relational/Query/SqlExpressions/ExistsExpression.cs index 21fd656abe0..aac6952f23f 100644 --- a/src/EFCore.Relational/Query/SqlExpressions/ExistsExpression.cs +++ b/src/EFCore.Relational/Query/SqlExpressions/ExistsExpression.cs @@ -18,11 +18,9 @@ public class ExistsExpression : SqlExpression /// Creates a new instance of the class. /// /// A subquery to check existence of. - /// A value indicating if the existence check is negated. /// The associated with the expression. public ExistsExpression( SelectExpression subquery, - bool negated, RelationalTypeMapping? typeMapping) : base(typeof(bool), typeMapping) { @@ -33,7 +31,6 @@ public ExistsExpression( } #endif Subquery = subquery; - IsNegated = negated; } /// @@ -41,22 +38,10 @@ public ExistsExpression( /// public virtual SelectExpression Subquery { get; } - /// - /// The value indicating if the existence check is negated. - /// - public virtual bool IsNegated { get; } - /// protected override Expression VisitChildren(ExpressionVisitor visitor) => Update((SelectExpression)visitor.Visit(Subquery)); - /// - /// Negates this expression by changing presence/absence state indicated by . - /// - /// An expression which is negated form of this expression. - public virtual ExistsExpression Negate() - => new(Subquery, !IsNegated, TypeMapping); - /// /// Creates a new expression that is like this one, but using the supplied children. If all of the children are the same, it will /// return this expression. @@ -65,17 +50,12 @@ public virtual ExistsExpression Negate() /// This expression if no children changed, or an expression with the updated children. public virtual ExistsExpression Update(SelectExpression subquery) => subquery != Subquery - ? new ExistsExpression(subquery, IsNegated, TypeMapping) + ? new ExistsExpression(subquery, TypeMapping) : this; /// protected override void Print(ExpressionPrinter expressionPrinter) { - if (IsNegated) - { - expressionPrinter.Append("NOT "); - } - expressionPrinter.AppendLine("EXISTS ("); using (expressionPrinter.Indent()) { @@ -94,10 +74,9 @@ public override bool Equals(object? obj) private bool Equals(ExistsExpression existsExpression) => base.Equals(existsExpression) - && Subquery.Equals(existsExpression.Subquery) - && IsNegated == existsExpression.IsNegated; + && Subquery.Equals(existsExpression.Subquery); /// public override int GetHashCode() - => HashCode.Combine(base.GetHashCode(), Subquery, IsNegated); + => HashCode.Combine(base.GetHashCode(), Subquery); } diff --git a/src/EFCore.Relational/Query/SqlExpressions/InExpression.cs b/src/EFCore.Relational/Query/SqlExpressions/InExpression.cs index af0777b47bb..525599a81ad 100644 --- a/src/EFCore.Relational/Query/SqlExpressions/InExpression.cs +++ b/src/EFCore.Relational/Query/SqlExpressions/InExpression.cs @@ -21,14 +21,12 @@ public class InExpression : SqlExpression /// /// An item to look into values. /// A subquery in which item is searched. - /// A value indicating if the item should be present in the values or absent. /// The associated with the expression. public InExpression( SqlExpression item, SelectExpression subquery, - bool negated, RelationalTypeMapping typeMapping) - : this(item, null, subquery, negated, typeMapping) + : this(item, null, subquery, typeMapping) { } @@ -37,14 +35,12 @@ public InExpression( /// /// An item to look into values. /// A list of values in which item is searched. - /// A value indicating if the item should be present in the values or absent. /// The associated with the expression. public InExpression( SqlExpression item, SqlExpression values, - bool negated, RelationalTypeMapping typeMapping) - : this(item, values, null, negated, typeMapping) + : this(item, values, null, typeMapping) { } @@ -52,7 +48,6 @@ private InExpression( SqlExpression item, SqlExpression? values, SelectExpression? subquery, - bool negated, RelationalTypeMapping? typeMapping) : base(typeof(bool), typeMapping) { @@ -65,7 +60,6 @@ private InExpression( Item = item; Subquery = subquery; Values = values; - IsNegated = negated; } /// @@ -73,11 +67,6 @@ private InExpression( /// public virtual SqlExpression Item { get; } - /// - /// The value indicating if item should be present in the values or absent. - /// - public virtual bool IsNegated { get; } - /// /// The list of values to search item in. /// @@ -98,13 +87,6 @@ protected override Expression VisitChildren(ExpressionVisitor visitor) return Update(item, values, subquery); } - /// - /// Negates this expression by changing presence/absence state indicated by . - /// - /// An expression which is negated form of this expression. - public virtual InExpression Negate() - => new(Item, Values, Subquery, !IsNegated, TypeMapping); - /// /// Creates a new expression that is like this one, but using the supplied children. If all of the children are the same, it will /// return this expression. @@ -125,7 +107,7 @@ public virtual InExpression Update( } return item != Item || subquery != Subquery || values != Values - ? new InExpression(item, values, subquery, IsNegated, TypeMapping) + ? new InExpression(item, values, subquery, TypeMapping) : this; } @@ -133,7 +115,7 @@ public virtual InExpression Update( protected override void Print(ExpressionPrinter expressionPrinter) { expressionPrinter.Visit(Item); - expressionPrinter.Append(IsNegated ? " NOT IN " : " IN "); + expressionPrinter.Append(" IN "); expressionPrinter.Append("("); if (Subquery != null) @@ -176,11 +158,10 @@ public override bool Equals(object? obj) private bool Equals(InExpression inExpression) => base.Equals(inExpression) && Item.Equals(inExpression.Item) - && IsNegated.Equals(inExpression.IsNegated) && (Values?.Equals(inExpression.Values) ?? inExpression.Values == null) && (Subquery?.Equals(inExpression.Subquery) ?? inExpression.Subquery == null); /// public override int GetHashCode() - => HashCode.Combine(base.GetHashCode(), Item, IsNegated, Values, Subquery); + => HashCode.Combine(base.GetHashCode(), Item, Values, Subquery); } diff --git a/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs b/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs index d0815c0bb99..e8d8a36c6a7 100644 --- a/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs +++ b/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs @@ -1868,8 +1868,7 @@ public void ApplyPredicate(SqlExpression sqlExpression) sqlExpression = PushdownIntoSubqueryInternal().Remap(sqlExpression); } - if ((sqlExpression is SqlBinaryExpression { OperatorType: ExpressionType.Equal } - || sqlExpression is InExpression { Subquery: null, IsNegated: false }) + if (sqlExpression is SqlBinaryExpression { OperatorType: ExpressionType.Equal } or InExpression { Subquery: null } && _groupBy.Count == 0) { // If the intersection is empty then we don't remove predicate so that the filter empty out all results. diff --git a/src/EFCore.Relational/Query/SqlNullabilityProcessor.cs b/src/EFCore.Relational/Query/SqlNullabilityProcessor.cs index 909d9daa921..42c4a4f9363 100644 --- a/src/EFCore.Relational/Query/SqlNullabilityProcessor.cs +++ b/src/EFCore.Relational/Query/SqlNullabilityProcessor.cs @@ -634,9 +634,9 @@ protected virtual SqlExpression VisitExists( nullable = false; // if subquery has predicate which evaluates to false, we can simply return false - // if the exisits is negated we need to return true instead + // if the exists is negated we need to return true instead return TryGetBoolConstantValue(subquery.Predicate) == false - ? _sqlExpressionFactory.Constant(existsExpression.IsNegated, existsExpression.TypeMapping) + ? _sqlExpressionFactory.Constant(false, existsExpression.TypeMapping) : existsExpression.Update(subquery); } @@ -651,9 +651,6 @@ protected virtual SqlExpression VisitIn(InExpression inExpression, bool allowOpt { var item = Visit(inExpression.Item, out var itemNullable); - // If the InExpression is negated, it's as if we have an enclosing NOT, which prohibits optimized expansion. - allowOptimizedExpansion &= !inExpression.IsNegated; - if (inExpression.Subquery != null) { var subquery = Visit(inExpression.Subquery); @@ -663,7 +660,7 @@ protected virtual SqlExpression VisitIn(InExpression inExpression, bool allowOpt { nullable = false; - return _sqlExpressionFactory.Constant(inExpression.IsNegated, inExpression.TypeMapping); + return _sqlExpressionFactory.Constant(false, inExpression.TypeMapping); } // Check whether the subquery projects out a nullable value; note that we unwrap any casts to get to the underlying @@ -718,9 +715,7 @@ protected virtual SqlExpression VisitIn(InExpression inExpression, bool allowOpt // WHERE Nullable NOT IN (SELECT NonNullable FROM foo) -> WHERE Nullable NOT IN (SELECT NonNullable FROM foo) OR Nullable IS NULL return allowOptimizedExpansion ? inExpression - : inExpression.IsNegated - ? _sqlExpressionFactory.OrElse(inExpression, _sqlExpressionFactory.IsNull(item)) - : _sqlExpressionFactory.AndAlso(inExpression, _sqlExpressionFactory.IsNotNull(item)); + : _sqlExpressionFactory.AndAlso(inExpression, _sqlExpressionFactory.IsNotNull(item)); } case (false, true): @@ -741,10 +736,7 @@ protected virtual SqlExpression VisitIn(InExpression inExpression, bool allowOpt goto TransformToExists; } - return inExpression.IsNegated - ? _sqlExpressionFactory.Not( - _sqlExpressionFactory.Coalesce(inExpression.Negate(), _sqlExpressionFactory.Constant(false))) - : _sqlExpressionFactory.Coalesce(inExpression, _sqlExpressionFactory.Constant(false)); + return _sqlExpressionFactory.Coalesce(inExpression, _sqlExpressionFactory.Constant(false)); } case (true, true): @@ -772,7 +764,7 @@ protected virtual SqlExpression VisitIn(InExpression inExpression, bool allowOpt subquery.ApplyPredicate(predicate); subquery.ClearOrdering(); - return _sqlExpressionFactory.Exists(subquery, inExpression.IsNegated); + return _sqlExpressionFactory.Exists(subquery); } } @@ -803,17 +795,10 @@ protected virtual SqlExpression VisitIn(InExpression inExpression, bool allowOpt // a IN () -> false // non_nullable IN (NULL) -> false - // a NOT IN () -> true - // non_nullable NOT IN (NULL) -> true // nullable IN (NULL) -> nullable IS NULL - // nullable NOT IN (NULL) -> nullable IS NOT NULL return !hasNullValue || !itemNullable - ? _sqlExpressionFactory.Constant( - inExpression.IsNegated, - inExpression.TypeMapping) - : inExpression.IsNegated - ? _sqlExpressionFactory.IsNotNull(item) - : _sqlExpressionFactory.IsNull(item); + ? _sqlExpressionFactory.Constant(false, inExpression.TypeMapping) + : _sqlExpressionFactory.IsNull(item); } var simplifiedInExpression = SimplifyInExpression( @@ -822,14 +807,12 @@ protected virtual SqlExpression VisitIn(InExpression inExpression, bool allowOpt inValuesList); if (!itemNullable - || (allowOptimizedExpansion && !inExpression.IsNegated && !hasNullValue)) + || (allowOptimizedExpansion && !hasNullValue)) { nullable = false; // non_nullable IN (1, 2) -> non_nullable IN (1, 2) // non_nullable IN (1, 2, NULL) -> non_nullable IN (1, 2) - // non_nullable NOT IN (1, 2) -> non_nullable NOT IN (1, 2) - // non_nullable NOT IN (1, 2, NULL) -> non_nullable NOT IN (1, 2) // nullable IN (1, 2) -> nullable IN (1, 2) (optimized) return simplifiedInExpression; } @@ -838,15 +821,9 @@ protected virtual SqlExpression VisitIn(InExpression inExpression, bool allowOpt // nullable IN (1, 2) -> nullable IN (1, 2) AND nullable IS NOT NULL (full) // nullable IN (1, 2, NULL) -> nullable IN (1, 2) OR nullable IS NULL (full) - // nullable NOT IN (1, 2) -> nullable NOT IN (1, 2) OR nullable IS NULL (full) - // nullable NOT IN (1, 2, NULL) -> nullable NOT IN (1, 2) AND nullable IS NOT NULL (full) - return inExpression.IsNegated == hasNullValue - ? _sqlExpressionFactory.AndAlso( - simplifiedInExpression, - _sqlExpressionFactory.IsNotNull(item)) - : _sqlExpressionFactory.OrElse( - simplifiedInExpression, - _sqlExpressionFactory.IsNull(item)); + return hasNullValue + ? _sqlExpressionFactory.OrElse(simplifiedInExpression, _sqlExpressionFactory.IsNull(item)) + : _sqlExpressionFactory.AndAlso(simplifiedInExpression, _sqlExpressionFactory.IsNotNull(item)); (SqlConstantExpression ProcessedValuesExpression, List ProcessedValuesList, bool HasNullValue) ProcessInExpressionValues(SqlExpression valuesExpression, bool extractNullValues) @@ -895,13 +872,9 @@ SqlExpression SimplifyInExpression( SqlConstantExpression inValuesExpression, List inValuesList) => inValuesList.Count == 1 - ? inExpression.IsNegated - ? (SqlExpression)_sqlExpressionFactory.NotEqual( - inExpression.Item, - _sqlExpressionFactory.Constant(inValuesList[0], inValuesExpression.TypeMapping)) - : _sqlExpressionFactory.Equal( - inExpression.Item, - _sqlExpressionFactory.Constant(inValuesList[0], inExpression.Values!.TypeMapping)) + ? _sqlExpressionFactory.Equal( + inExpression.Item, + _sqlExpressionFactory.Constant(inValuesList[0], inExpression.Values!.TypeMapping)) : inExpression; } @@ -1722,15 +1695,11 @@ protected virtual SqlExpression OptimizeNonNullableNotExpression(SqlUnaryExpress { // !(true) -> false // !(false) -> true - case SqlConstantExpression constantOperand - when constantOperand.Value is bool value: + case SqlConstantExpression { Value: bool value }: { return _sqlExpressionFactory.Constant(!value, sqlUnaryExpression.TypeMapping); } - case InExpression inOperand: - return inOperand.Negate(); - case SqlUnaryExpression sqlUnaryOperand: { switch (sqlUnaryOperand.OperatorType) diff --git a/src/EFCore.Sqlite.Core/Query/Internal/SqliteQuerySqlGenerator.cs b/src/EFCore.Sqlite.Core/Query/Internal/SqliteQuerySqlGenerator.cs index 6ebd4a515c4..8e0622a64ae 100644 --- a/src/EFCore.Sqlite.Core/Query/Internal/SqliteQuerySqlGenerator.cs +++ b/src/EFCore.Sqlite.Core/Query/Internal/SqliteQuerySqlGenerator.cs @@ -32,12 +32,21 @@ public SqliteQuerySqlGenerator(QuerySqlGeneratorDependencies dependencies) /// doing so can result in application failures when updating to a new Entity Framework Core release. /// protected override Expression VisitExtension(Expression extensionExpression) - => extensionExpression switch + { + switch (extensionExpression) { - GlobExpression globExpression => VisitGlob(globExpression), - RegexpExpression regexpExpression => VisitRegexp(regexpExpression), - _ => base.VisitExtension(extensionExpression) - }; + case GlobExpression globExpression: + GenerateGlob(globExpression); + return extensionExpression; + + case RegexpExpression regexpExpression: + GenerateRegexp(regexpExpression); + return extensionExpression; + + default: + return base.VisitExtension(extensionExpression); + } + } /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -88,34 +97,30 @@ protected override void GenerateSetOperationOperand(SetOperationBase setOperatio // Sqlite doesn't support parentheses around set operation operands => Visit(operand); - private Expression VisitGlob(GlobExpression globExpression) + private void GenerateGlob(GlobExpression globExpression, bool negated = false) { Visit(globExpression.Match); - if (globExpression.IsNegated) + if (negated) { Sql.Append(" NOT"); } Sql.Append(" GLOB "); Visit(globExpression.Pattern); - - return globExpression; } - private Expression VisitRegexp(RegexpExpression regexpExpression) + private void GenerateRegexp(RegexpExpression regexpExpression, bool negated = false) { Visit(regexpExpression.Match); - if (regexpExpression.IsNegated) + if (negated) { Sql.Append(" NOT"); } Sql.Append(" REGEXP "); Visit(regexpExpression.Pattern); - - return regexpExpression; } /// @@ -225,6 +230,34 @@ protected override Expression VisitJsonScalar(JsonScalarExpression jsonScalarExp return jsonScalarExpression; } + /// + /// 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. + /// + protected override Expression VisitSqlUnary(SqlUnaryExpression sqlUnaryExpression) + { + switch (sqlUnaryExpression.OperatorType) + { + case ExpressionType.Not when sqlUnaryExpression.Type == typeof(bool): + switch (sqlUnaryExpression.Operand) + { + case GlobExpression globExpression: + GenerateGlob(globExpression, negated: true); + return sqlUnaryExpression; + + case RegexpExpression regexpExpression: + GenerateRegexp(regexpExpression, negated: true); + return sqlUnaryExpression; + } + goto default; + + default: + return base.VisitSqlUnary(sqlUnaryExpression); + } + } + /// /// 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 diff --git a/src/EFCore.Sqlite.Core/Query/Internal/SqliteSqlExpressionFactory.cs b/src/EFCore.Sqlite.Core/Query/Internal/SqliteSqlExpressionFactory.cs index 86e7df0a6ee..c58f11be60b 100644 --- a/src/EFCore.Sqlite.Core/Query/Internal/SqliteSqlExpressionFactory.cs +++ b/src/EFCore.Sqlite.Core/Query/Internal/SqliteSqlExpressionFactory.cs @@ -117,7 +117,7 @@ public virtual SqlFunctionExpression Date( /// 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. /// - public virtual GlobExpression Glob(SqlExpression match, SqlExpression pattern, bool negated = false) + public virtual GlobExpression Glob(SqlExpression match, SqlExpression pattern) { var inferredTypeMapping = ExpressionExtensions.InferTypeMapping(match, pattern) ?? Dependencies.TypeMappingSource.FindMapping(match.Type, Dependencies.Model); @@ -125,7 +125,7 @@ public virtual GlobExpression Glob(SqlExpression match, SqlExpression pattern, b match = ApplyTypeMapping(match, inferredTypeMapping); pattern = ApplyTypeMapping(pattern, inferredTypeMapping); - return new GlobExpression(match, pattern, negated, _boolTypeMapping); + return new GlobExpression(match, pattern, _boolTypeMapping); } /// @@ -134,7 +134,7 @@ public virtual GlobExpression Glob(SqlExpression match, SqlExpression pattern, b /// 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. /// - public virtual RegexpExpression Regexp(SqlExpression match, SqlExpression pattern, bool negated = false) + public virtual RegexpExpression Regexp(SqlExpression match, SqlExpression pattern) { var inferredTypeMapping = ExpressionExtensions.InferTypeMapping(match, pattern) ?? Dependencies.TypeMappingSource.FindMapping(match.Type, Dependencies.Model); @@ -142,7 +142,7 @@ public virtual RegexpExpression Regexp(SqlExpression match, SqlExpression patter match = ApplyTypeMapping(match, inferredTypeMapping); pattern = ApplyTypeMapping(pattern, inferredTypeMapping); - return new RegexpExpression(match, pattern, negated, _boolTypeMapping); + return new RegexpExpression(match, pattern, _boolTypeMapping); } /// @@ -171,7 +171,7 @@ private SqlExpression ApplyTypeMappingOnGlob(GlobExpression globExpression) var pattern = ApplyTypeMapping(globExpression.Pattern, inferredTypeMapping); return match != globExpression.Match || pattern != globExpression.Pattern || globExpression.TypeMapping != _boolTypeMapping - ? new GlobExpression(match, pattern, globExpression.IsNegated, _boolTypeMapping) + ? new GlobExpression(match, pattern, _boolTypeMapping) : globExpression; } @@ -184,7 +184,7 @@ private SqlExpression ApplyTypeMappingOnGlob(GlobExpression globExpression) var pattern = ApplyTypeMapping(regexpExpression.Pattern, inferredTypeMapping); return match != regexpExpression.Match || pattern != regexpExpression.Pattern || regexpExpression.TypeMapping != _boolTypeMapping - ? new RegexpExpression(match, pattern, regexpExpression.IsNegated, _boolTypeMapping) + ? new RegexpExpression(match, pattern, _boolTypeMapping) : regexpExpression; } } diff --git a/src/EFCore.Sqlite.Core/Query/Internal/SqliteSqlNullabilityProcessor.cs b/src/EFCore.Sqlite.Core/Query/Internal/SqliteSqlNullabilityProcessor.cs index b5144975ade..9385ab82464 100644 --- a/src/EFCore.Sqlite.Core/Query/Internal/SqliteSqlNullabilityProcessor.cs +++ b/src/EFCore.Sqlite.Core/Query/Internal/SqliteSqlNullabilityProcessor.cs @@ -83,27 +83,4 @@ protected virtual SqlExpression VisitRegexp( return regexpExpression.Update(match, pattern); } - - /// - /// 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. - /// - protected override SqlExpression OptimizeNonNullableNotExpression(SqlUnaryExpression sqlUnaryExpression) - { - if (sqlUnaryExpression.OperatorType == ExpressionType.Not) - { - switch (sqlUnaryExpression.Operand) - { - case GlobExpression globOperand: - return globOperand.Negate(); - - case RegexpExpression regexpOperand: - return regexpOperand.Negate(); - } - } - - return base.OptimizeNonNullableNotExpression(sqlUnaryExpression); - } } diff --git a/src/EFCore.Sqlite.Core/Query/SqlExpressions/Internal/GlobExpression.cs b/src/EFCore.Sqlite.Core/Query/SqlExpressions/Internal/GlobExpression.cs index f8349759558..1901cd67b4a 100644 --- a/src/EFCore.Sqlite.Core/Query/SqlExpressions/Internal/GlobExpression.cs +++ b/src/EFCore.Sqlite.Core/Query/SqlExpressions/Internal/GlobExpression.cs @@ -19,12 +19,11 @@ public class GlobExpression : SqlExpression /// 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. /// - public GlobExpression(SqlExpression match, SqlExpression pattern, bool negated, RelationalTypeMapping typeMapping) + public GlobExpression(SqlExpression match, SqlExpression pattern, RelationalTypeMapping typeMapping) : base(typeof(bool), typeMapping) { Match = match; Pattern = pattern; - IsNegated = negated; } /// @@ -52,14 +51,6 @@ public override RelationalTypeMapping TypeMapping /// public virtual SqlExpression Pattern { get; } - /// - /// 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. - /// - public virtual bool IsNegated { get; } - /// /// 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 @@ -74,15 +65,6 @@ protected override Expression VisitChildren(ExpressionVisitor visitor) return Update(match, pattern); } - /// - /// 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. - /// - public virtual GlobExpression Negate() - => new GlobExpression(Match, Pattern, !IsNegated, TypeMapping); - /// /// 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 @@ -91,7 +73,7 @@ public virtual GlobExpression Negate() /// public virtual GlobExpression Update(SqlExpression match, SqlExpression pattern) => match != Match || pattern != Pattern - ? new GlobExpression(match, pattern, IsNegated, TypeMapping) + ? new GlobExpression(match, pattern, TypeMapping) : this; /// @@ -103,12 +85,6 @@ public virtual GlobExpression Update(SqlExpression match, SqlExpression pattern) protected override void Print(ExpressionPrinter expressionPrinter) { expressionPrinter.Visit(Match); - - if (IsNegated) - { - expressionPrinter.Append(" NOT"); - } - expressionPrinter.Append(" GLOB "); expressionPrinter.Visit(Pattern); } @@ -128,8 +104,7 @@ public override bool Equals(object? obj) private bool Equals(GlobExpression globExpression) => base.Equals(globExpression) && Match.Equals(globExpression.Match) - && Pattern.Equals(globExpression.Pattern) - && IsNegated.Equals(globExpression.IsNegated); + && Pattern.Equals(globExpression.Pattern); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -138,5 +113,5 @@ private bool Equals(GlobExpression globExpression) /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public override int GetHashCode() - => HashCode.Combine(base.GetHashCode(), Match, Pattern, IsNegated); + => HashCode.Combine(base.GetHashCode(), Match, Pattern); } diff --git a/src/EFCore.Sqlite.Core/Query/SqlExpressions/Internal/RegexpExpression.cs b/src/EFCore.Sqlite.Core/Query/SqlExpressions/Internal/RegexpExpression.cs index c06634d33b9..101cced8c7c 100644 --- a/src/EFCore.Sqlite.Core/Query/SqlExpressions/Internal/RegexpExpression.cs +++ b/src/EFCore.Sqlite.Core/Query/SqlExpressions/Internal/RegexpExpression.cs @@ -19,12 +19,11 @@ public class RegexpExpression : SqlExpression /// 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. /// - public RegexpExpression(SqlExpression match, SqlExpression pattern, bool negated, RelationalTypeMapping typeMapping) + public RegexpExpression(SqlExpression match, SqlExpression pattern, RelationalTypeMapping typeMapping) : base(typeof(bool), typeMapping) { Match = match; Pattern = pattern; - IsNegated = negated; } /// @@ -52,14 +51,6 @@ public override RelationalTypeMapping TypeMapping /// public virtual SqlExpression Pattern { get; } - /// - /// 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. - /// - public virtual bool IsNegated { get; } - /// /// 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 @@ -74,15 +65,6 @@ protected override Expression VisitChildren(ExpressionVisitor visitor) return Update(match, pattern); } - /// - /// 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. - /// - public virtual RegexpExpression Negate() - => new RegexpExpression(Match, Pattern, !IsNegated, TypeMapping); - /// /// 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 @@ -91,7 +73,7 @@ public virtual RegexpExpression Negate() /// public virtual RegexpExpression Update(SqlExpression match, SqlExpression pattern) => match != Match || pattern != Pattern - ? new RegexpExpression(match, pattern, IsNegated, TypeMapping) + ? new RegexpExpression(match, pattern, TypeMapping) : this; /// @@ -103,12 +85,6 @@ public virtual RegexpExpression Update(SqlExpression match, SqlExpression patter protected override void Print(ExpressionPrinter expressionPrinter) { expressionPrinter.Visit(Match); - - if (IsNegated) - { - expressionPrinter.Append(" NOT"); - } - expressionPrinter.Append(" REGEXP "); expressionPrinter.Visit(Pattern); } @@ -128,8 +104,7 @@ public override bool Equals(object? obj) private bool Equals(RegexpExpression regexpExpression) => base.Equals(regexpExpression) && Match.Equals(regexpExpression.Match) - && Pattern.Equals(regexpExpression.Pattern) - && IsNegated.Equals(regexpExpression.IsNegated); + && Pattern.Equals(regexpExpression.Pattern); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -138,5 +113,5 @@ private bool Equals(RegexpExpression regexpExpression) /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public override int GetHashCode() - => HashCode.Combine(base.GetHashCode(), Match, Pattern, IsNegated); + => HashCode.Combine(base.GetHashCode(), Match, Pattern); } diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindAggregateOperatorsQueryCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindAggregateOperatorsQueryCosmosTest.cs index d0a06c59c3b..ef420efcda6 100644 --- a/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindAggregateOperatorsQueryCosmosTest.cs +++ b/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindAggregateOperatorsQueryCosmosTest.cs @@ -1395,7 +1395,7 @@ public override async Task Contains_with_local_collection_false(bool async) """ SELECT c FROM root c -WHERE ((c["Discriminator"] = "Customer") AND NOT(c["CustomerID"] IN ("ABCDE", "ALFKI"))) +WHERE ((c["Discriminator"] = "Customer") AND c["CustomerID"] NOT IN ("ABCDE", "ALFKI")) """); } @@ -1431,7 +1431,7 @@ public override async Task Contains_with_local_collection_complex_predicate_not_ """ SELECT c FROM root c -WHERE ((c["Discriminator"] = "Customer") AND (((c["CustomerID"] = "ALFKI") OR (c["CustomerID"] = "ABCDE")) OR NOT(c["CustomerID"] IN ("ABCDE", "ALFKI")))) +WHERE ((c["Discriminator"] = "Customer") AND (((c["CustomerID"] = "ALFKI") OR (c["CustomerID"] = "ABCDE")) OR c["CustomerID"] NOT IN ("ABCDE", "ALFKI"))) """); } @@ -1790,7 +1790,7 @@ public override async Task Where_subquery_all_not_equals_operator(bool async) """ SELECT c FROM root c -WHERE ((c["Discriminator"] = "Customer") AND NOT(c["CustomerID"] IN ("ABCDE", "ALFKI", "ANATR"))) +WHERE ((c["Discriminator"] = "Customer") AND c["CustomerID"] NOT IN ("ABCDE", "ALFKI", "ANATR")) """); } @@ -1802,7 +1802,7 @@ public override async Task Where_subquery_all_not_equals(bool async) """ SELECT c FROM root c -WHERE ((c["Discriminator"] = "Customer") AND NOT(c["CustomerID"] IN ("ABCDE", "ALFKI", "ANATR"))) +WHERE ((c["Discriminator"] = "Customer") AND c["CustomerID"] NOT IN ("ABCDE", "ALFKI", "ANATR")) """); } @@ -1814,7 +1814,7 @@ public override async Task Where_subquery_all_not_equals_static(bool async) """ SELECT c FROM root c -WHERE ((c["Discriminator"] = "Customer") AND NOT(c["CustomerID"] IN ("ABCDE", "ALFKI", "ANATR"))) +WHERE ((c["Discriminator"] = "Customer") AND c["CustomerID"] NOT IN ("ABCDE", "ALFKI", "ANATR")) """); } @@ -1826,13 +1826,13 @@ public override async Task Where_subquery_where_all(bool async) """ SELECT c FROM root c -WHERE (((c["Discriminator"] = "Customer") AND (c["City"] = "México D.F.")) AND NOT(c["CustomerID"] IN ("ABCDE", "ALFKI", "ANATR"))) +WHERE (((c["Discriminator"] = "Customer") AND (c["City"] = "México D.F.")) AND c["CustomerID"] NOT IN ("ABCDE", "ALFKI", "ANATR")) """, // """ SELECT c FROM root c -WHERE (((c["Discriminator"] = "Customer") AND (c["City"] = "México D.F.")) AND NOT(c["CustomerID"] IN ("ABCDE", "ALFKI", "ANATR"))) +WHERE (((c["Discriminator"] = "Customer") AND (c["City"] = "México D.F.")) AND c["CustomerID"] NOT IN ("ABCDE", "ALFKI", "ANATR")) """); } diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindWhereQueryCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindWhereQueryCosmosTest.cs index cea41b36843..349c3abd31a 100644 --- a/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindWhereQueryCosmosTest.cs +++ b/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindWhereQueryCosmosTest.cs @@ -2723,7 +2723,7 @@ public override async Task Constant_array_Contains_AndAlso_another_Contains_gets """ SELECT c FROM root c -WHERE ((c["Discriminator"] = "Customer") AND (NOT(c["CustomerID"] IN ("ALFKI", "ANATR")) AND NOT(c["CustomerID"] IN ("ALFKI", "ANTON")))) +WHERE ((c["Discriminator"] = "Customer") AND (c["CustomerID"] NOT IN ("ALFKI", "ANATR") AND c["CustomerID"] NOT IN ("ALFKI", "ANTON"))) """); } diff --git a/test/EFCore.Relational.Specification.Tests/Query/UdfDbFunctionTestBase.cs b/test/EFCore.Relational.Specification.Tests/Query/UdfDbFunctionTestBase.cs index 980b287dfcd..88e9f2aa50c 100644 --- a/test/EFCore.Relational.Specification.Tests/Query/UdfDbFunctionTestBase.cs +++ b/test/EFCore.Relational.Specification.Tests/Query/UdfDbFunctionTestBase.cs @@ -297,7 +297,6 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) args => new InExpression( args.First(), new SqlConstantExpression(Expression.Constant(abc), typeMapping: null), // args.First().TypeMapping), - negated: false, typeMapping: null)); var trueFalse = new[] { true, false }; @@ -307,10 +306,8 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) new InExpression( args.First(), new SqlConstantExpression(Expression.Constant(abc), args.First().TypeMapping), - negated: false, typeMapping: null), new SqlConstantExpression(Expression.Constant(trueFalse), typeMapping: null), - negated: false, typeMapping: null)); modelBuilder.HasDbFunction(typeof(UDFSqlContext).GetMethod(nameof(NullableValueReturnType), Array.Empty())) diff --git a/test/EFCore.Specification.Tests/ApiConsistencyTestBase.cs b/test/EFCore.Specification.Tests/ApiConsistencyTestBase.cs index c901a2ccb97..2604d0bab6e 100644 --- a/test/EFCore.Specification.Tests/ApiConsistencyTestBase.cs +++ b/test/EFCore.Specification.Tests/ApiConsistencyTestBase.cs @@ -875,7 +875,7 @@ where type.IsVisible from method in type.GetMethods(AnyInstance) where method.DeclaringType == type && !Fixture.NonVirtualMethods.Contains(method) - && (!method.IsVirtual || method.IsFinal) + && !method.IsVirtual && !method.Name.StartsWith("add_", StringComparison.Ordinal) && !method.Name.StartsWith("remove_", StringComparison.Ordinal) && !method.Name.Equals("get_NodeType", StringComparison.Ordinal) diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/FunkyDataQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/FunkyDataQuerySqlServerTest.cs index c554afea882..ea890b609a0 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/FunkyDataQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/FunkyDataQuerySqlServerTest.cs @@ -56,7 +56,7 @@ WHERE [f].[FirstName] LIKE N'%\_Ba\_%' ESCAPE N'\' """ SELECT [f].[FirstName] FROM [FunkyCustomers] AS [f] -WHERE NOT ([f].[FirstName] LIKE N'%\%B\%a\%r%' ESCAPE N'\') +WHERE [f].[FirstName] NOT LIKE N'%\%B\%a\%r%' ESCAPE N'\' """, // """ @@ -68,7 +68,7 @@ FROM [FunkyCustomers] AS [f] """ SELECT [f].[FirstName] FROM [FunkyCustomers] AS [f] -WHERE NOT ([f].[FirstName] LIKE NULL) +WHERE [f].[FirstName] NOT LIKE NULL """); } @@ -201,7 +201,7 @@ WHERE [f].[FirstName] IS NOT NULL AND [f].[FirstName] LIKE N'\_Ba\_%' ESCAPE N'\ """ SELECT [f].[FirstName] FROM [FunkyCustomers] AS [f] -WHERE [f].[FirstName] IS NOT NULL AND NOT ([f].[FirstName] LIKE N'\%B\%a\%r%' ESCAPE N'\') +WHERE [f].[FirstName] IS NOT NULL AND [f].[FirstName] NOT LIKE N'\%B\%a\%r%' ESCAPE N'\' """, // """ @@ -400,7 +400,7 @@ WHERE [f].[FirstName] IS NOT NULL AND [f].[FirstName] LIKE N'%\_Ba\_' ESCAPE N'\ """ SELECT [f].[FirstName] FROM [FunkyCustomers] AS [f] -WHERE [f].[FirstName] IS NOT NULL AND NOT ([f].[FirstName] LIKE N'%\%B\%a\%r' ESCAPE N'\') +WHERE [f].[FirstName] IS NOT NULL AND [f].[FirstName] NOT LIKE N'%\%B\%a\%r' ESCAPE N'\' """, // """ diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/ManyToManyNoTrackingQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/ManyToManyNoTrackingQuerySqlServerTest.cs index 3b3e3026fa0..81b10793389 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/ManyToManyNoTrackingQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/ManyToManyNoTrackingQuerySqlServerTest.cs @@ -28,7 +28,7 @@ WHERE NOT EXISTS ( SELECT 1 FROM [JoinOneToTwo] AS [j] INNER JOIN [EntityTwos] AS [e0] ON [j].[TwoId] = [e0].[Id] - WHERE [e].[Id] = [j].[OneId] AND NOT ([e0].[Name] LIKE N'%B%')) + WHERE [e].[Id] = [j].[OneId] AND [e0].[Name] NOT LIKE N'%B%') """); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/ManyToManyQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/ManyToManyQuerySqlServerTest.cs index 09a38b7c6d5..a9f35df4498 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/ManyToManyQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/ManyToManyQuerySqlServerTest.cs @@ -27,7 +27,7 @@ WHERE NOT EXISTS ( SELECT 1 FROM [JoinOneToTwo] AS [j] INNER JOIN [EntityTwos] AS [e0] ON [j].[TwoId] = [e0].[Id] - WHERE [e].[Id] = [j].[OneId] AND NOT ([e0].[Name] LIKE N'%B%')) + WHERE [e].[Id] = [j].[OneId] AND [e0].[Name] NOT LIKE N'%B%') """); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindFunctionsQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindFunctionsQuerySqlServerTest.cs index d8aa23243aa..7a4cc086669 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindFunctionsQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindFunctionsQuerySqlServerTest.cs @@ -2353,7 +2353,7 @@ public override async Task IsNullOrEmpty_negated_in_predicate(bool async) """ SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[Region] IS NOT NULL AND NOT ([c].[Region] LIKE N'') +WHERE [c].[Region] IS NOT NULL AND [c].[Region] NOT LIKE N'' """); } @@ -2364,7 +2364,7 @@ public override async Task IsNullOrEmpty_negated_in_projection(bool async) AssertSql( """ SELECT [c].[CustomerID] AS [Id], CASE - WHEN [c].[Region] IS NOT NULL AND NOT ([c].[Region] LIKE N'') THEN CAST(1 AS bit) + WHEN [c].[Region] IS NOT NULL AND [c].[Region] NOT LIKE N'' THEN CAST(1 AS bit) ELSE CAST(0 AS bit) END AS [Value] FROM [Customers] AS [c] diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindMiscellaneousQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindMiscellaneousQuerySqlServerTest.cs index f59f8df4023..4611175ad8e 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindMiscellaneousQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindMiscellaneousQuerySqlServerTest.cs @@ -1609,7 +1609,7 @@ SELECT CASE WHEN NOT EXISTS ( SELECT 1 FROM [Customers] AS [c] - WHERE [c].[ContactName] IS NULL OR NOT ([c].[ContactName] LIKE N'A%')) THEN CAST(1 AS bit) + WHERE [c].[ContactName] IS NULL OR [c].[ContactName] NOT LIKE N'A%') THEN CAST(1 AS bit) ELSE CAST(0 AS bit) END """); @@ -1641,13 +1641,13 @@ SELECT CASE WHEN NOT EXISTS ( SELECT 1 FROM [Customers] AS [c] - WHERE NOT (EXISTS ( + WHERE NOT EXISTS ( SELECT 1 FROM [Customers] AS [c0] WHERE EXISTS ( SELECT 1 FROM [Customers] AS [c1] - WHERE [c].[CustomerID] = [c1].[CustomerID])))) THEN CAST(1 AS bit) + WHERE [c].[CustomerID] = [c1].[CustomerID]))) THEN CAST(1 AS bit) ELSE CAST(0 AS bit) END """); @@ -1663,13 +1663,13 @@ SELECT CASE WHEN NOT EXISTS ( SELECT 1 FROM [Customers] AS [c] - WHERE NOT (EXISTS ( + WHERE NOT EXISTS ( SELECT 1 FROM [Customers] AS [c0] WHERE EXISTS ( SELECT 1 FROM [Customers] AS [c1] - WHERE [c].[CustomerID] = [c1].[CustomerID])))) THEN CAST(1 AS bit) + WHERE [c].[CustomerID] = [c1].[CustomerID]))) THEN CAST(1 AS bit) ELSE CAST(0 AS bit) END """); @@ -2244,7 +2244,7 @@ FROM [Customers] AS [c] ORDER BY [c].[CustomerID] OFFSET @__p_0 ROWS FETCH NEXT @__p_1 ROWS ONLY ) AS [t] - WHERE NOT ([t].[CustomerID] LIKE N'B%')) THEN CAST(1 AS bit) + WHERE [t].[CustomerID] NOT LIKE N'B%') THEN CAST(1 AS bit) ELSE CAST(0 AS bit) END """); @@ -2266,7 +2266,7 @@ SELECT TOP(@__p_0) [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName FROM [Customers] AS [c] ORDER BY [c].[CustomerID] ) AS [t] - WHERE NOT ([t].[CustomerID] LIKE N'A%')) THEN CAST(1 AS bit) + WHERE [t].[CustomerID] NOT LIKE N'A%') THEN CAST(1 AS bit) ELSE CAST(0 AS bit) END """); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/OperatorsQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/OperatorsQuerySqlServerTest.cs index 0b30a5f2f55..585d88799b2 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/OperatorsQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/OperatorsQuerySqlServerTest.cs @@ -124,7 +124,7 @@ public override async Task Negate_on_like_expression(bool async) """ SELECT [o].[Id] FROM [OperatorEntityString] AS [o] -WHERE [o].[Value] IS NOT NULL AND NOT ([o].[Value] LIKE N'A%') +WHERE [o].[Value] IS NOT NULL AND [o].[Value] NOT LIKE N'A%' """); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/TPCManyToManyNoTrackingQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/TPCManyToManyNoTrackingQuerySqlServerTest.cs index 6173122c1d1..4ab77ad4be6 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/TPCManyToManyNoTrackingQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/TPCManyToManyNoTrackingQuerySqlServerTest.cs @@ -31,7 +31,7 @@ WHERE NOT EXISTS ( SELECT 1 FROM [JoinOneToTwo] AS [j] INNER JOIN [EntityTwos] AS [e0] ON [j].[TwoId] = [e0].[Id] - WHERE [e].[Id] = [j].[OneId] AND NOT ([e0].[Name] LIKE N'%B%')) + WHERE [e].[Id] = [j].[OneId] AND [e0].[Name] NOT LIKE N'%B%') """); } @@ -2135,7 +2135,7 @@ WHERE NOT EXISTS ( SELECT 1 FROM [UnidirectionalJoinOneToTwo] AS [u0] INNER JOIN [UnidirectionalEntityTwos] AS [u1] ON [u0].[TwoId] = [u1].[Id] - WHERE [u].[Id] = [u0].[OneId] AND NOT ([u1].[Name] LIKE N'%B%')) + WHERE [u].[Id] = [u0].[OneId] AND [u1].[Name] NOT LIKE N'%B%') """); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/TPCManyToManyQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/TPCManyToManyQuerySqlServerTest.cs index f3dc0fbb67c..2da39428122 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/TPCManyToManyQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/TPCManyToManyQuerySqlServerTest.cs @@ -31,7 +31,7 @@ WHERE NOT EXISTS ( SELECT 1 FROM [JoinOneToTwo] AS [j] INNER JOIN [EntityTwos] AS [e0] ON [j].[TwoId] = [e0].[Id] - WHERE [e].[Id] = [j].[OneId] AND NOT ([e0].[Name] LIKE N'%B%')) + WHERE [e].[Id] = [j].[OneId] AND [e0].[Name] NOT LIKE N'%B%') """); } @@ -2136,7 +2136,7 @@ WHERE NOT EXISTS ( SELECT 1 FROM [UnidirectionalJoinOneToTwo] AS [u0] INNER JOIN [UnidirectionalEntityTwos] AS [u1] ON [u0].[TwoId] = [u1].[Id] - WHERE [u].[Id] = [u0].[OneId] AND NOT ([u1].[Name] LIKE N'%B%')) + WHERE [u].[Id] = [u0].[OneId] AND [u1].[Name] NOT LIKE N'%B%') """); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/TPTManyToManyNoTrackingQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/TPTManyToManyNoTrackingQuerySqlServerTest.cs index 2a949c57378..7a4b3d9246c 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/TPTManyToManyNoTrackingQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/TPTManyToManyNoTrackingQuerySqlServerTest.cs @@ -32,7 +32,7 @@ WHERE NOT EXISTS ( SELECT 1 FROM [JoinOneToTwo] AS [j] INNER JOIN [EntityTwos] AS [e0] ON [j].[TwoId] = [e0].[Id] - WHERE [e].[Id] = [j].[OneId] AND NOT ([e0].[Name] LIKE N'%B%')) + WHERE [e].[Id] = [j].[OneId] AND [e0].[Name] NOT LIKE N'%B%') """); } @@ -2087,7 +2087,7 @@ WHERE NOT EXISTS ( SELECT 1 FROM [UnidirectionalJoinOneToTwo] AS [u0] INNER JOIN [UnidirectionalEntityTwos] AS [u1] ON [u0].[TwoId] = [u1].[Id] - WHERE [u].[Id] = [u0].[OneId] AND NOT ([u1].[Name] LIKE N'%B%')) + WHERE [u].[Id] = [u0].[OneId] AND [u1].[Name] NOT LIKE N'%B%') """); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/TPTManyToManyQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/TPTManyToManyQuerySqlServerTest.cs index 6090d1e01af..fb2c9a9b4a8 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/TPTManyToManyQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/TPTManyToManyQuerySqlServerTest.cs @@ -31,7 +31,7 @@ WHERE NOT EXISTS ( SELECT 1 FROM [JoinOneToTwo] AS [j] INNER JOIN [EntityTwos] AS [e0] ON [j].[TwoId] = [e0].[Id] - WHERE [e].[Id] = [j].[OneId] AND NOT ([e0].[Name] LIKE N'%B%')) + WHERE [e].[Id] = [j].[OneId] AND [e0].[Name] NOT LIKE N'%B%') """); } @@ -2087,7 +2087,7 @@ WHERE NOT EXISTS ( SELECT 1 FROM [UnidirectionalJoinOneToTwo] AS [u0] INNER JOIN [UnidirectionalEntityTwos] AS [u1] ON [u0].[TwoId] = [u1].[Id] - WHERE [u].[Id] = [u0].[OneId] AND NOT ([u1].[Name] LIKE N'%B%')) + WHERE [u].[Id] = [u0].[OneId] AND [u1].[Name] NOT LIKE N'%B%') """); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/TemporalManyToManyQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/TemporalManyToManyQuerySqlServerTest.cs index fc448965ee6..7d92da9b856 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/TemporalManyToManyQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/TemporalManyToManyQuerySqlServerTest.cs @@ -60,7 +60,7 @@ WHERE NOT EXISTS ( SELECT 1 FROM [JoinOneToTwo] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [j] INNER JOIN [EntityTwos] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [e0] ON [j].[TwoId] = [e0].[Id] - WHERE [e].[Id] = [j].[OneId] AND NOT ([e0].[Name] LIKE N'%B%')) + WHERE [e].[Id] = [j].[OneId] AND [e0].[Name] NOT LIKE N'%B%') """); } diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/OperatorsQuerySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/OperatorsQuerySqliteTest.cs index 91a74647a6d..4943509ad3b 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/OperatorsQuerySqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/OperatorsQuerySqliteTest.cs @@ -116,7 +116,7 @@ public override async Task Negate_on_like_expression(bool async) """ SELECT "o"."Id" FROM "OperatorEntityString" AS "o" -WHERE "o"."Value" IS NOT NULL AND NOT ("o"."Value" LIKE 'A%') +WHERE "o"."Value" IS NOT NULL AND "o"."Value" NOT LIKE 'A%' """); }