From 47586855631a87a4716a00c6e6661693546d3bab Mon Sep 17 00:00:00 2001 From: Shay Rojansky Date: Tue, 15 Aug 2023 21:35:44 +0200 Subject: [PATCH] Fully implement element nullability for primitive collections (#31468) Closes #31416 --- ...yableMethodTranslatingExpressionVisitor.cs | 28 +- ...lationalSqlTranslatingExpressionVisitor.cs | 253 ++++++++++-------- ...yableMethodTranslatingExpressionVisitor.cs | 17 +- ...yableMethodTranslatingExpressionVisitor.cs | 15 +- .../Conventions/NonNullableConventionBase.cs | 16 +- .../NonNullableNavigationConvention.cs | 3 +- .../NonNullableReferencePropertyConvention.cs | 19 +- .../JsonTypesCosmosTest.cs | 17 ++ .../JsonTypesTestBase.cs | 24 +- .../PrimitiveCollectionsQueryTestBase.cs | 92 +++++-- ...imitiveCollectionsQueryOldSqlServerTest.cs | 75 ++++-- .../PrimitiveCollectionsQuerySqlServerTest.cs | 205 +++++++++----- .../PrimitiveCollectionsQuerySqliteTest.cs | 190 ++++++++----- ...NullableReferencePropertyConventionTest.cs | 22 ++ .../ModelBuilding/NonRelationshipTestBase.cs | 2 +- 15 files changed, 641 insertions(+), 337 deletions(-) diff --git a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs index 72786e94826..7f36326f67c 100644 --- a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs @@ -219,7 +219,7 @@ when entityQueryRootExpression.GetType() == typeof(EntityQueryRootExpression) Check.DebugAssert(sqlParameterExpression is not null, "sqlParameterExpression is not null"); return TranslateCollection( sqlParameterExpression, - elementTypeMapping: null, + property: null, char.ToLowerInvariant(sqlParameterExpression.Name.First(c => c != '_')).ToString()) ?? base.VisitExtension(extensionExpression); @@ -265,20 +265,24 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp if (translated == QueryCompilationContext.NotTranslatedExpression) { // Attempt to translate access into a primitive collection property (i.e. array column) - if (_sqlTranslator.TryTranslatePropertyAccess(methodCallExpression, out var propertyAccessExpression) - && propertyAccessExpression is SqlExpression + + // TODO: We should be detecting primitive collections by looking at GetElementType() of the property and not at its type + // mapping; but #31469 is blocking that for shadow properties. + if (_sqlTranslator.TryTranslatePropertyAccess(methodCallExpression, out var translatedExpression, out var property) + && property is IProperty regularProperty + && translatedExpression is SqlExpression { - TypeMapping.ElementTypeMapping: RelationalTypeMapping elementTypeMapping - } collectionPropertyAccessExpression) + TypeMapping.ElementTypeMapping: RelationalTypeMapping + } sqlExpression) { - var tableAlias = collectionPropertyAccessExpression switch + var tableAlias = sqlExpression switch { ColumnExpression c => c.Name[..1].ToLowerInvariant(), JsonScalarExpression { Path: [.., { PropertyName: string propertyName }] } => propertyName[..1].ToLowerInvariant(), _ => "j" }; - if (TranslateCollection(collectionPropertyAccessExpression, elementTypeMapping, tableAlias) is + if (TranslateCollection(sqlExpression, regularProperty, tableAlias) is { } primitiveCollectionTranslation) { return primitiveCollectionTranslation; @@ -316,17 +320,15 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp /// collections. /// /// The expression to try to translate as a primitive collection expression. - /// - /// The type mapping of the collection's element, or when it's not known (i.e. for parameters). + /// + /// If the primitive collection is a property, contains the for that property. Otherwise, the collection + /// represents a parameter, and this contains . /// /// /// Provides an alias to be used for the table returned from translation, which will represent the collection. /// /// A if the translation was successful, otherwise . - protected virtual ShapedQueryExpression? TranslateCollection( - SqlExpression sqlExpression, - RelationalTypeMapping? elementTypeMapping, - string tableAlias) + protected virtual ShapedQueryExpression? TranslateCollection(SqlExpression sqlExpression, IProperty? property, string tableAlias) => null; /// diff --git a/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs b/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs index a0c62e8a1ab..88716980b18 100644 --- a/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs @@ -547,16 +547,13 @@ SqlExpression GeneratePredicateTpt(StructuralTypeProjectionExpression projection else { var discriminatorColumn = BindProperty(typeReference, discriminatorProperty); - if (discriminatorColumn != null) - { - return match - ? _sqlExpressionFactory.Equal( - discriminatorColumn, - _sqlExpressionFactory.Constant(derivedType.GetDiscriminatorValue())) - : _sqlExpressionFactory.NotEqual( - discriminatorColumn, - _sqlExpressionFactory.Constant(derivedType.GetDiscriminatorValue())); - } + return match + ? _sqlExpressionFactory.Equal( + discriminatorColumn, + _sqlExpressionFactory.Constant(derivedType.GetDiscriminatorValue())) + : _sqlExpressionFactory.NotEqual( + discriminatorColumn, + _sqlExpressionFactory.Constant(derivedType.GetDiscriminatorValue())); } return QueryCompilationContext.NotTranslatedExpression; @@ -739,8 +736,9 @@ protected override Expression VisitMember(MemberExpression memberExpression) { var innerExpression = Visit(memberExpression.Expression); - return TryBindMember(innerExpression, MemberIdentity.Create(memberExpression.Member)) - ?? (TranslationFailed(memberExpression.Expression, innerExpression, out var sqlInnerExpression) + return TryBindMember(innerExpression, MemberIdentity.Create(memberExpression.Member), out var expression) + ? expression + : (TranslationFailed(memberExpression.Expression, innerExpression, out var sqlInnerExpression) ? QueryCompilationContext.NotTranslatedExpression : Dependencies.MemberTranslatorProvider.Translate( sqlInnerExpression, memberExpression.Member, memberExpression.Type, _queryCompilationContext.Logger)) @@ -760,26 +758,28 @@ protected override Expression VisitMemberInit(MemberInitExpression memberInitExp /// doing so can result in application failures when updating to a new Entity Framework Core release. /// [EntityFrameworkInternal] - public virtual bool TryTranslatePropertyAccess(Expression expression, [NotNullWhen(true)] out Expression? propertyAccessExpression) + public virtual bool TryTranslatePropertyAccess( + Expression expression, + [NotNullWhen(true)] out Expression? translatedExpression, + [NotNullWhen(true)] out IPropertyBase? property) { if (expression is MethodCallExpression methodCallExpression) { if (methodCallExpression.TryGetEFPropertyArguments(out var source, out var propertyName) - && TryBindMember(Visit(source), MemberIdentity.Create(propertyName)) is Expression result) + && TryBindMember(Visit(source), MemberIdentity.Create(propertyName), out translatedExpression, out property)) { - propertyAccessExpression = result; return true; } if (methodCallExpression.TryGetIndexerArguments(_model, out source, out propertyName) - && TryBindMember(Visit(source), MemberIdentity.Create(propertyName)) is Expression indexerResult) + && TryBindMember(Visit(source), MemberIdentity.Create(propertyName), out translatedExpression, out property)) { - propertyAccessExpression = indexerResult; return true; } } - propertyAccessExpression = null; + translatedExpression = null; + property = null; return false; } @@ -789,7 +789,7 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp // EF.Property case if (methodCallExpression.TryGetEFPropertyArguments(out var source, out var propertyName)) { - if (TryBindMember(Visit(source), MemberIdentity.Create(propertyName)) is Expression result) + if (TryBindMember(Visit(source), MemberIdentity.Create(propertyName), out var result)) { return result; } @@ -807,7 +807,7 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp // EF Indexer property if (methodCallExpression.TryGetIndexerArguments(_model, out source, out propertyName) - && TryBindMember(Visit(source), MemberIdentity.Create(propertyName)) is Expression indexerResult) + && TryBindMember(Visit(source), MemberIdentity.Create(propertyName), out var indexerResult)) { return indexerResult; } @@ -1129,21 +1129,16 @@ SqlExpression GeneratePredicateTpt(StructuralTypeProjectionExpression entityProj { var concreteEntityTypes = derivedType.GetConcreteDerivedTypesInclusive().ToList(); var discriminatorColumn = BindProperty(typeReference, discriminatorProperty); - if (discriminatorColumn != null) - { - return concreteEntityTypes.Count == 1 - ? _sqlExpressionFactory.Equal( - discriminatorColumn, - _sqlExpressionFactory.Constant(concreteEntityTypes[0].GetDiscriminatorValue())) - : _sqlExpressionFactory.In( - discriminatorColumn, - concreteEntityTypes.Select(et => _sqlExpressionFactory.Constant(et.GetDiscriminatorValue())).ToArray()); - } - } - else - { - return _sqlExpressionFactory.Constant(true); + return concreteEntityTypes.Count == 1 + ? _sqlExpressionFactory.Equal( + discriminatorColumn, + _sqlExpressionFactory.Constant(concreteEntityTypes[0].GetDiscriminatorValue())) + : _sqlExpressionFactory.In( + discriminatorColumn, + concreteEntityTypes.Select(et => _sqlExpressionFactory.Constant(et.GetDiscriminatorValue())).ToArray()); } + + return _sqlExpressionFactory.Constant(true); } return QueryCompilationContext.NotTranslatedExpression; @@ -1207,22 +1202,36 @@ protected override Expression VisitUnary(UnaryExpression unaryExpression) return QueryCompilationContext.NotTranslatedExpression; } - private Expression? TryBindMember(Expression? source, MemberIdentity member) + private bool TryBindMember( + Expression? source, + MemberIdentity member, + [NotNullWhen(true)] out Expression? expression) + => TryBindMember(source, member, out expression, out _); + + private bool TryBindMember( + Expression? source, + MemberIdentity member, + [NotNullWhen(true)] out Expression? expression, + [NotNullWhen(true)] out IPropertyBase? property) { if (source is not StructuralTypeReferenceExpression typeReference) { - return null; + expression = null; + property = null; + return false; } var structuralType = typeReference.StructuralType; - var property = member.MemberInfo != null + var regularProperty = member.MemberInfo != null ? structuralType.FindProperty(member.MemberInfo) : structuralType.FindProperty(member.Name!); - if (property != null) + if (regularProperty != null) { - return BindProperty(typeReference, property); + expression = BindProperty(typeReference, regularProperty); + property = regularProperty; + return true; } var complexProperty = member.MemberInfo != null @@ -1231,7 +1240,9 @@ protected override Expression VisitUnary(UnaryExpression unaryExpression) if (complexProperty is not null) { - return BindComplexProperty(typeReference, complexProperty); + expression = BindComplexProperty(typeReference, complexProperty); + property = complexProperty; + return true; } AddTranslationErrorDetails( @@ -1239,103 +1250,109 @@ protected override Expression VisitUnary(UnaryExpression unaryExpression) member.Name, typeReference.StructuralType.DisplayName())); - return null; + expression = null; + property = null; + return false; } - private SqlExpression? BindProperty(StructuralTypeReferenceExpression typeReference, IProperty property) + private SqlExpression BindProperty(StructuralTypeReferenceExpression typeReference, IProperty property) { - if (typeReference.Parameter != null) + switch (typeReference) { - var valueBufferExpression = Visit(typeReference.Parameter.ValueBufferExpression); - if (valueBufferExpression is JsonQueryExpression jsonQueryExpression) + case { Parameter: StructuralTypeShaperExpression shaper }: { - return jsonQueryExpression.BindProperty(property); - } + var valueBufferExpression = Visit(shaper.ValueBufferExpression); + if (valueBufferExpression is JsonQueryExpression jsonQueryExpression) + { + return jsonQueryExpression.BindProperty(property); + } - var projection = (StructuralTypeProjectionExpression)valueBufferExpression; - var propertyAccess = projection.BindProperty(property); + var projection = (StructuralTypeProjectionExpression)valueBufferExpression; + var propertyAccess = projection.BindProperty(property); - if (typeReference.StructuralType is not IEntityType entityType - || entityType.FindDiscriminatorProperty() != null - || entityType.FindPrimaryKey() == null - || entityType.GetRootType() != entityType - || entityType.GetMappingStrategy() == RelationalAnnotationNames.TpcMappingStrategy) - { - return propertyAccess; - } + if (typeReference.StructuralType is not IEntityType entityType + || entityType.FindDiscriminatorProperty() != null + || entityType.FindPrimaryKey() == null + || entityType.GetRootType() != entityType + || entityType.GetMappingStrategy() == RelationalAnnotationNames.TpcMappingStrategy) + { + return propertyAccess; + } - var table = entityType.GetViewOrTableMappings().SingleOrDefault(e => e.IsSplitEntityTypePrincipal ?? true)?.Table - ?? entityType.GetDefaultMappings().Single().Table; - if (!table.IsOptional(entityType)) - { - return propertyAccess; - } + var table = entityType.GetViewOrTableMappings().SingleOrDefault(e => e.IsSplitEntityTypePrincipal ?? true)?.Table + ?? entityType.GetDefaultMappings().Single().Table; + if (!table.IsOptional(entityType)) + { + return propertyAccess; + } - // this is optional dependent sharing table - var nonPrincipalSharedNonPkProperties = entityType.GetNonPrincipalSharedNonPkProperties(table); - if (nonPrincipalSharedNonPkProperties.Contains(property)) - { - // The column is not being shared with principal side so we can always use directly - return propertyAccess; - } + // this is optional dependent sharing table + var nonPrincipalSharedNonPkProperties = entityType.GetNonPrincipalSharedNonPkProperties(table); + if (nonPrincipalSharedNonPkProperties.Contains(property)) + { + // The column is not being shared with principal side so we can always use directly + return propertyAccess; + } - SqlExpression? condition = null; - // Property is being shared with principal side, so we need to make it conditional access - var allRequiredNonPkProperties = - entityType.GetProperties().Where(p => !p.IsNullable && !p.IsPrimaryKey()).ToList(); - if (allRequiredNonPkProperties.Count > 0) - { - condition = allRequiredNonPkProperties.Select(p => projection.BindProperty(p)) - .Select(c => (SqlExpression)_sqlExpressionFactory.NotEqual(c, _sqlExpressionFactory.Constant(null))) - .Aggregate((a, b) => _sqlExpressionFactory.AndAlso(a, b)); - } + SqlExpression? condition = null; + // Property is being shared with principal side, so we need to make it conditional access + var allRequiredNonPkProperties = + entityType.GetProperties().Where(p => !p.IsNullable && !p.IsPrimaryKey()).ToList(); + if (allRequiredNonPkProperties.Count > 0) + { + condition = allRequiredNonPkProperties.Select(p => projection.BindProperty(p)) + .Select(c => (SqlExpression)_sqlExpressionFactory.NotEqual(c, _sqlExpressionFactory.Constant(null))) + .Aggregate((a, b) => _sqlExpressionFactory.AndAlso(a, b)); + } - if (nonPrincipalSharedNonPkProperties.Count != 0 - && nonPrincipalSharedNonPkProperties.All(p => p.IsNullable)) - { - // If all non principal shared properties are nullable then we need additional condition - var atLeastOneNonNullValueInNullableColumnsCondition = nonPrincipalSharedNonPkProperties - .Select(p => projection.BindProperty(p)) - .Select(c => (SqlExpression)_sqlExpressionFactory.NotEqual(c, _sqlExpressionFactory.Constant(null))) - .Aggregate((a, b) => _sqlExpressionFactory.OrElse(a, b)); - - condition = condition == null - ? atLeastOneNonNullValueInNullableColumnsCondition - : _sqlExpressionFactory.AndAlso(condition, atLeastOneNonNullValueInNullableColumnsCondition); - } + if (nonPrincipalSharedNonPkProperties.Count != 0 + && nonPrincipalSharedNonPkProperties.All(p => p.IsNullable)) + { + // If all non principal shared properties are nullable then we need additional condition + var atLeastOneNonNullValueInNullableColumnsCondition = nonPrincipalSharedNonPkProperties + .Select(p => projection.BindProperty(p)) + .Select(c => (SqlExpression)_sqlExpressionFactory.NotEqual(c, _sqlExpressionFactory.Constant(null))) + .Aggregate((a, b) => _sqlExpressionFactory.OrElse(a, b)); + + condition = condition == null + ? atLeastOneNonNullValueInNullableColumnsCondition + : _sqlExpressionFactory.AndAlso(condition, atLeastOneNonNullValueInNullableColumnsCondition); + } - if (condition == null) - { - // if we cannot compute condition then we just return property access (and hope for the best) - return propertyAccess; - } + if (condition == null) + { + // if we cannot compute condition then we just return property access (and hope for the best) + return propertyAccess; + } - return _sqlExpressionFactory.Case( - new List { new(condition, propertyAccess) }, - elseResult: null); + return _sqlExpressionFactory.Case( + new List { new(condition, propertyAccess) }, + elseResult: null); - // We don't do above processing for subquery entity since it comes from after subquery which has been - // single result so either it is regular entity or a collection which always have their own table. - } + // We don't do above processing for subquery entity since it comes from after subquery which has been + // single result so either it is regular entity or a collection which always have their own table. + } - if (typeReference.Subquery != null) - { - var entityShaper = (StructuralTypeShaperExpression)typeReference.Subquery.ShaperExpression; - var subSelectExpression = (SelectExpression)typeReference.Subquery.QueryExpression; + case { Subquery: ShapedQueryExpression subquery }: + { + var entityShaper = (StructuralTypeShaperExpression)subquery.ShaperExpression; + var subSelectExpression = (SelectExpression)subquery.QueryExpression; - var projectionBindingExpression = (ProjectionBindingExpression)entityShaper.ValueBufferExpression; - var projection = (StructuralTypeProjectionExpression)subSelectExpression.GetProjection(projectionBindingExpression); - var innerProjection = projection.BindProperty(property); - subSelectExpression.ReplaceProjection(new List { innerProjection }); - subSelectExpression.ApplyProjection(); + var projectionBindingExpression = (ProjectionBindingExpression)entityShaper.ValueBufferExpression; + var projection = (StructuralTypeProjectionExpression)subSelectExpression.GetProjection(projectionBindingExpression); + var innerProjection = projection.BindProperty(property); + subSelectExpression.ReplaceProjection(new List { innerProjection }); + subSelectExpression.ApplyProjection(); - return new ScalarSubqueryExpression(subSelectExpression); - } + return new ScalarSubqueryExpression(subSelectExpression); + } - return null; + default: + throw new UnreachableException(); + } } - private StructuralTypeReferenceExpression? BindComplexProperty( + private StructuralTypeReferenceExpression BindComplexProperty( StructuralTypeReferenceExpression typeReference, IComplexProperty complexProperty) { @@ -1352,7 +1369,7 @@ protected override Expression VisitUnary(UnaryExpression unaryExpression) throw new InvalidOperationException(); // TODO: Figure this out; do we support it? default: - return null; + throw new UnreachableException(); } } diff --git a/src/EFCore.SqlServer/Query/Internal/SqlServerQueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore.SqlServer/Query/Internal/SqlServerQueryableMethodTranslatingExpressionVisitor.cs index d35d676055a..050489a2270 100644 --- a/src/EFCore.SqlServer/Query/Internal/SqlServerQueryableMethodTranslatingExpressionVisitor.cs +++ b/src/EFCore.SqlServer/Query/Internal/SqlServerQueryableMethodTranslatingExpressionVisitor.cs @@ -129,7 +129,7 @@ protected override Expression VisitExtension(Expression extensionExpression) /// protected override ShapedQueryExpression? TranslateCollection( SqlExpression sqlExpression, - RelationalTypeMapping? elementTypeMapping, + IProperty? property, string tableAlias) { if (_sqlServerCompatibilityLevel < 130) @@ -145,6 +145,8 @@ protected override Expression VisitExtension(Expression extensionExpression) // (i.e. with a columnInfo), which determines the type conversion to apply to the JSON elements coming out. // For parameter collections, the element type mapping will only be inferred and applied later (see // SqlServerInferredTypeMappingApplier below), at which point the we'll apply it to add the WITH clause. + var elementTypeMapping = (RelationalTypeMapping?)sqlExpression.TypeMapping?.ElementTypeMapping; + var openJsonExpression = elementTypeMapping is null ? new SqlServerOpenJsonExpression(tableAlias, sqlExpression) : new SqlServerOpenJsonExpression( @@ -159,9 +161,16 @@ protected override Expression VisitExtension(Expression extensionExpression) } }); - // TODO: This is a temporary CLR type-based check; when we have proper metadata to determine if the element is nullable, use it here var elementClrType = sqlExpression.Type.GetSequenceType(); - var isColumnNullable = elementClrType.IsNullableType(); + + // If this is a collection property, get the element's nullability out of metadata. Otherwise, this is a parameter property, in + // which case we only have the CLR type (note that we cannot produce different SQLs based on the nullability of an *element* in + // a parameter collection - our caching mechanism only supports varying by the nullability of the parameter itself (i.e. the + // collection). + // TODO: if property is non-null, GetElementType() should never be null, but we have #31469 for shadow properties + var isElementNullable = property?.GetElementType() is null + ? elementClrType.IsNullableType() + : property.GetElementType()!.IsNullable; #pragma warning disable EF1001 // Internal EF Core API usage. var selectExpression = new SelectExpression( @@ -169,7 +178,7 @@ protected override Expression VisitExtension(Expression extensionExpression) columnName: "value", columnType: elementClrType, columnTypeMapping: elementTypeMapping, - isColumnNullable, + isElementNullable, identifierColumnName: "key", identifierColumnType: typeof(string), identifierColumnTypeMapping: _typeMappingSource.FindMapping("nvarchar(4000)")); diff --git a/src/EFCore.Sqlite.Core/Query/Internal/SqliteQueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore.Sqlite.Core/Query/Internal/SqliteQueryableMethodTranslatingExpressionVisitor.cs index 2e64f9aa569..6ef34247808 100644 --- a/src/EFCore.Sqlite.Core/Query/Internal/SqliteQueryableMethodTranslatingExpressionVisitor.cs +++ b/src/EFCore.Sqlite.Core/Query/Internal/SqliteQueryableMethodTranslatingExpressionVisitor.cs @@ -203,7 +203,7 @@ protected override QueryableMethodTranslatingExpressionVisitor CreateSubqueryVis /// protected override ShapedQueryExpression? TranslateCollection( SqlExpression sqlExpression, - RelationalTypeMapping? elementTypeMapping, + IProperty? property, string tableAlias) { // Support for JSON functions (e.g. json_each) was added in Sqlite 3.38.0 (2022-02-22, see https://www.sqlite.org/json1.html). @@ -215,11 +215,18 @@ protected override QueryableMethodTranslatingExpressionVisitor CreateSubqueryVis return null; } + var elementTypeMapping = (RelationalTypeMapping?)sqlExpression.TypeMapping?.ElementTypeMapping; var elementClrType = sqlExpression.Type.GetSequenceType(); var jsonEachExpression = new JsonEachExpression(tableAlias, sqlExpression); - // TODO: This is a temporary CLR type-based check; when we have proper metadata to determine if the element is nullable, use it here - var isColumnNullable = elementClrType.IsNullableType(); + // If this is a collection property, get the element's nullability out of metadata. Otherwise, this is a parameter property, in + // which case we only have the CLR type (note that we cannot produce different SQLs based on the nullability of an *element* in + // a parameter collection - our caching mechanism only supports varying by the nullability of the parameter itself (i.e. the + // collection). + // TODO: if property is non-null, GetElementType() should never be null, but we have #31469 for shadow properties + var isElementNullable = property?.GetElementType() is null + ? elementClrType.IsNullableType() + : property.GetElementType()!.IsNullable; #pragma warning disable EF1001 // Internal EF Core API usage. var selectExpression = new SelectExpression( @@ -227,7 +234,7 @@ protected override QueryableMethodTranslatingExpressionVisitor CreateSubqueryVis columnName: "value", columnType: elementClrType, columnTypeMapping: elementTypeMapping, - isColumnNullable, + isElementNullable, identifierColumnName: "key", identifierColumnType: typeof(int), identifierColumnTypeMapping: _typeMappingSource.FindMapping(typeof(int))); diff --git a/src/EFCore/Metadata/Conventions/NonNullableConventionBase.cs b/src/EFCore/Metadata/Conventions/NonNullableConventionBase.cs index 80055aa0fdf..fa5ed19ba2e 100644 --- a/src/EFCore/Metadata/Conventions/NonNullableConventionBase.cs +++ b/src/EFCore/Metadata/Conventions/NonNullableConventionBase.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; + namespace Microsoft.EntityFrameworkCore.Metadata.Conventions; /// @@ -33,13 +35,19 @@ protected NonNullableConventionBase(ProviderConventionSetBuilderDependencies dep /// /// The model builder used to build the model. /// The member info. + /// + /// The nullability info for the , or if it does not represent a valid reference + /// type. + /// /// if the member type is a non-nullable reference type. - protected virtual bool IsNonNullableReferenceType( + protected virtual bool TryGetNullabilityInfo( IConventionModelBuilder modelBuilder, - MemberInfo memberInfo) + MemberInfo memberInfo, + [NotNullWhen(true)] out NullabilityInfo? nullabilityInfo) { if (memberInfo.GetMemberType().IsValueType) { + nullabilityInfo = null; return false; } @@ -49,14 +57,14 @@ protected virtual bool IsNonNullableReferenceType( var nullabilityInfoContext = (NullabilityInfoContext)annotation.Value!; - var nullabilityInfo = memberInfo switch + nullabilityInfo = memberInfo switch { PropertyInfo propertyInfo => nullabilityInfoContext.Create(propertyInfo), FieldInfo fieldInfo => nullabilityInfoContext.Create(fieldInfo), _ => null }; - return nullabilityInfo?.ReadState == NullabilityState.NotNull; + return nullabilityInfo is not null; } /// diff --git a/src/EFCore/Metadata/Conventions/NonNullableNavigationConvention.cs b/src/EFCore/Metadata/Conventions/NonNullableNavigationConvention.cs index f0e58e886d6..66baa664c4c 100644 --- a/src/EFCore/Metadata/Conventions/NonNullableNavigationConvention.cs +++ b/src/EFCore/Metadata/Conventions/NonNullableNavigationConvention.cs @@ -77,5 +77,6 @@ private void ProcessNavigation(IConventionNavigationBuilder navigationBuilder) private bool IsNonNullable(IConventionModelBuilder modelBuilder, IConventionNavigation navigation) => navigation.DeclaringEntityType.GetRuntimeProperties().Find(navigation.Name) is PropertyInfo propertyInfo - && IsNonNullableReferenceType(modelBuilder, propertyInfo); + && TryGetNullabilityInfo(modelBuilder, propertyInfo, out var nullabilityInfo) + && nullabilityInfo.ReadState == NullabilityState.NotNull; } diff --git a/src/EFCore/Metadata/Conventions/NonNullableReferencePropertyConvention.cs b/src/EFCore/Metadata/Conventions/NonNullableReferencePropertyConvention.cs index 20e176575cf..d5c23f624df 100644 --- a/src/EFCore/Metadata/Conventions/NonNullableReferencePropertyConvention.cs +++ b/src/EFCore/Metadata/Conventions/NonNullableReferencePropertyConvention.cs @@ -29,16 +29,29 @@ public NonNullableReferencePropertyConvention(ProviderConventionSetBuilderDepend private void Process(IConventionPropertyBuilder propertyBuilder) { if (propertyBuilder.Metadata.GetIdentifyingMemberInfo() is MemberInfo memberInfo - && IsNonNullableReferenceType(propertyBuilder.ModelBuilder, memberInfo)) + && TryGetNullabilityInfo(propertyBuilder.ModelBuilder, memberInfo, out var nullabilityInfo)) { - propertyBuilder.IsRequired(true); + if (nullabilityInfo.ReadState == NullabilityState.NotNull) + { + propertyBuilder.IsRequired(true); + } + + // If there's an element type, this is a primitive collection; check and apply the element's nullability as well. + if (propertyBuilder.Metadata.GetElementType() is IConventionElementType elementType + && nullabilityInfo is + { ElementType.ReadState: NullabilityState.NotNull } or + { GenericTypeArguments: [{ ReadState: NullabilityState.NotNull }] }) + { + elementType.SetIsNullable(false); + } } } private void Process(IConventionComplexPropertyBuilder propertyBuilder) { if (propertyBuilder.Metadata.GetIdentifyingMemberInfo() is MemberInfo memberInfo - && IsNonNullableReferenceType(propertyBuilder.ModelBuilder, memberInfo)) + && TryGetNullabilityInfo(propertyBuilder.ModelBuilder, memberInfo, out var nullabilityInfo) + && nullabilityInfo.ReadState == NullabilityState.NotNull) { propertyBuilder.IsRequired(true); } diff --git a/test/EFCore.Cosmos.FunctionalTests/JsonTypesCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/JsonTypesCosmosTest.cs index 4891eb90128..ca54815dd7a 100644 --- a/test/EFCore.Cosmos.FunctionalTests/JsonTypesCosmosTest.cs +++ b/test/EFCore.Cosmos.FunctionalTests/JsonTypesCosmosTest.cs @@ -3,10 +3,27 @@ #nullable enable +using Xunit.Sdk; + namespace Microsoft.EntityFrameworkCore.Cosmos; public class JsonTypesCosmosTest : JsonTypesTestBase { + // #25765 - the Cosmos type mapping source doesn't support primitive collections, so we end up with a Property + // that has no ElementType; that causes the assertion on the element nullability to fail. + public override void Can_read_write_collection_of_string_JSON_values() + => Assert.Throws(() => base.Can_read_write_collection_of_string_JSON_values()); + + // #25765 - the Cosmos type mapping source doesn't support primitive collections, so we end up with a Property + // that has no ElementType; that causes the assertion on the element nullability to fail. + public override void Can_read_write_collection_of_binary_JSON_values() + => Assert.Throws(() => base.Can_read_write_collection_of_binary_JSON_values()); + + // #25765 - the Cosmos type mapping source doesn't support primitive collections, so we end up with a Property + // that has no ElementType; that causes the assertion on the element nullability to fail. + public override void Can_read_write_collection_of_nullable_string_JSON_values() + => Assert.Throws(() => base.Can_read_write_collection_of_nullable_string_JSON_values()); + public override void Can_read_write_point() // No built-in JSON support for spatial types in the Cosmos provider => Assert.Throws(() => base.Can_read_write_point()); diff --git a/test/EFCore.Specification.Tests/JsonTypesTestBase.cs b/test/EFCore.Specification.Tests/JsonTypesTestBase.cs index fd81eaf10be..bd222c099d9 100644 --- a/test/EFCore.Specification.Tests/JsonTypesTestBase.cs +++ b/test/EFCore.Specification.Tests/JsonTypesTestBase.cs @@ -3443,9 +3443,29 @@ protected virtual void Can_read_and_write_JSON_value( Assert.Equal(typeof(TModel).GetSequenceType(), element.ClrType); Assert.Same(property, element.CollectionProperty); - Assert.Equal(json.Contains("null", StringComparison.Ordinal) || !element.ClrType.IsValueType, element.IsNullable); Assert.Null(element.FindTypeMapping()!.ElementTypeMapping); + bool elementNullable; + if (element.ClrType.IsValueType) + { + elementNullable = element.ClrType.IsNullableType(); + } + else + { + var nullabilityInfo = property switch + { + { PropertyInfo: PropertyInfo p } => _nullabilityInfoContext.Create(p), + { FieldInfo: FieldInfo f } => _nullabilityInfoContext.Create(f), + _ => throw new UnreachableException() + }; + + elementNullable = nullabilityInfo is not + { ElementType.ReadState: NullabilityState.NotNull } and not + { GenericTypeArguments: [{ ReadState: NullabilityState.NotNull }] }; + } + + Assert.Equal(elementNullable, element.IsNullable); + var comparer = element.GetValueComparer()!; var elementReaderWriter = element.GetJsonValueReaderWriter()!; foreach (var item in (IEnumerable)value!) @@ -3741,4 +3761,6 @@ protected virtual void OnConfiguring(DbContextOptionsBuilder optionsBuilder) CoreEventId.MappedEntityTypeIgnoredWarning, CoreEventId.MappedPropertyIgnoredWarning, CoreEventId.MappedNavigationIgnoredWarning)); + + private readonly NullabilityInfoContext _nullabilityInfoContext = new(); } diff --git a/test/EFCore.Specification.Tests/Query/PrimitiveCollectionsQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/PrimitiveCollectionsQueryTestBase.cs index b54d968cc90..f6bc23f3eec 100644 --- a/test/EFCore.Specification.Tests/Query/PrimitiveCollectionsQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/PrimitiveCollectionsQueryTestBase.cs @@ -1,7 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Microsoft.EntityFrameworkCore.Diagnostics; +#nullable enable namespace Microsoft.EntityFrameworkCore.Query; @@ -207,7 +207,7 @@ public virtual Task Parameter_collection_of_nullable_ints_Contains_nullable_int( [ConditionalTheory] [MemberData(nameof(IsAsyncData))] - public virtual Task Parameter_collection_of_strings_Contains(bool async) + public virtual Task Parameter_collection_of_strings_Contains_non_nullable_string(bool async) { var strings = new[] { "10", "999" }; @@ -217,6 +217,18 @@ public virtual Task Parameter_collection_of_strings_Contains(bool async) entryCount: 1); } + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Parameter_collection_of_strings_Contains_nullable_string(bool async) + { + var strings = new[] { "999", null }; + + return AssertQuery( + async, + ss => ss.Set().Where(c => strings.Contains(c.NullableString)), + entryCount: 3); + } + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Parameter_collection_of_DateTimes_Contains(bool async) @@ -261,11 +273,11 @@ public virtual Task Parameter_collection_of_enums_Contains(bool async) [MemberData(nameof(IsAsyncData))] public virtual async Task Parameter_collection_null_Contains(bool async) { - int[] ints = null; + int[]? ints = null; await AssertQuery( async, - ss => ss.Set().Where(c => ints.Contains(c.Int)), + ss => ss.Set().Where(c => ints!.Contains(c.Int)), ss => ss.Set().Where(c => false)); } @@ -301,6 +313,14 @@ public virtual Task Column_collection_of_strings_contains_null(bool async) ss => ss.Set().Where(c => c.Strings.Contains(null)), entryCount: 0); + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Column_collection_of_nullable_strings_contains_null(bool async) + => AssertQuery( + async, + ss => ss.Set().Where(c => c.NullableStrings.Contains(null)), + entryCount: 3); + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Column_collection_of_bools_Contains(bool async) @@ -364,6 +384,28 @@ public virtual Task Column_collection_index_beyond_end(bool async) ss => ss.Set().Where(c => false), entryCount: 0); + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Nullable_reference_column_collection_index_equals_nullable_column(bool async) + => AssertQuery( + async, + ss => ss.Set() + .Where(c => c.NullableStrings[2] == c.NullableString), + ss => ss.Set() + .Where(c => (c.NullableStrings.Length > 2 ? c.NullableStrings[2] : default) == c.NullableString), + entryCount: 3); + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Non_nullable_reference_column_collection_index_equals_nullable_column(bool async) + => AssertQuery( + async, + ss => ss.Set() + .Where(c => c.Strings.Any() && c.Strings[1] == c.NullableString), + ss => ss.Set() + .Where(c => c.Strings.Any() && (c.Strings.Length > 1 ? c.Strings[1] : default) == c.NullableString), + entryCount: 1); + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Inline_collection_index_Column(bool async) @@ -834,7 +876,7 @@ public virtual Task Project_primitive_collections_element(bool async) public abstract class PrimitiveCollectionsQueryFixtureBase : SharedStoreFixtureBase, IQueryFixtureBase { - private PrimitiveArrayData _expectedData; + private PrimitiveArrayData? _expectedData; protected override string StoreName => "PrimitiveCollectionsTest"; @@ -851,12 +893,12 @@ protected override void Seed(PrimitiveCollectionsContext context) public virtual ISetSource GetExpectedData() => _expectedData ??= new PrimitiveArrayData(); - public IReadOnlyDictionary EntitySorters { get; } = new Dictionary> + public IReadOnlyDictionary EntitySorters { get; } = new Dictionary> { - { typeof(PrimitiveCollectionsEntity), e => ((PrimitiveCollectionsEntity)e)?.Id } - }.ToDictionary(e => e.Key, e => (object)e.Value); + { typeof(PrimitiveCollectionsEntity), e => ((PrimitiveCollectionsEntity?)e)?.Id } + }.ToDictionary(e => e.Key, e => (object?)e.Value); - public IReadOnlyDictionary EntityAsserters { get; } = new Dictionary> + public IReadOnlyDictionary EntityAsserters { get; } = new Dictionary> { { typeof(PrimitiveCollectionsEntity), (e, a) => @@ -865,7 +907,7 @@ public virtual ISetSource GetExpectedData() if (a != null) { - var ee = (PrimitiveCollectionsEntity)e; + var ee = (PrimitiveCollectionsEntity)e!; var aa = (PrimitiveCollectionsEntity)a; Assert.Equal(ee.Id, aa.Id); @@ -892,19 +934,21 @@ public class PrimitiveCollectionsEntity { public int Id { get; set; } - public string String { get; set; } + public required string String { get; set; } public int Int { get; set; } public DateTime DateTime { get; set; } public bool Bool { get; set; } public MyEnum Enum { get; set; } public int? NullableInt { get; set; } - - public string[] Strings { get; set; } - public int[] Ints { get; set; } - public DateTime[] DateTimes { get; set; } - public bool[] Bools { get; set; } - public MyEnum[] Enums { get; set; } - public int?[] NullableInts { get; set; } + public string? NullableString { get; set; } + + public required string[] Strings { get; set; } + public required int[] Ints { get; set; } + public required DateTime[] DateTimes { get; set; } + public required bool[] Bools { get; set; } + public required MyEnum[] Enums { get; set; } + public required int?[] NullableInts { get; set; } + public required string?[] NullableStrings { get; set; } } public enum MyEnum { Value1, Value2, Value3, Value4 } @@ -913,7 +957,7 @@ public class PrimitiveArrayData : ISetSource { public IReadOnlyList PrimitiveArrayEntities { get; } - public PrimitiveArrayData(PrimitiveCollectionsContext context = null) + public PrimitiveArrayData(PrimitiveCollectionsContext? context = null) { PrimitiveArrayEntities = CreatePrimitiveArrayEntities(); @@ -948,6 +992,7 @@ private static IReadOnlyList CreatePrimitiveArrayEnt Bool = true, Enum = MyEnum.Value1, NullableInt = 10, + NullableString = "10", Ints = new[] { 1, 10 }, Strings = new[] { "1", "10" }, @@ -959,6 +1004,7 @@ private static IReadOnlyList CreatePrimitiveArrayEnt Bools = new[] { true, false }, Enums = new[] { MyEnum.Value1, MyEnum.Value2 }, NullableInts = new int?[] { 1, 10 }, + NullableStrings = new[] { "1", "10" } }, new() { @@ -970,6 +1016,7 @@ private static IReadOnlyList CreatePrimitiveArrayEnt Bool = false, Enum = MyEnum.Value2, NullableInt = null, + NullableString = null, Ints = new[] { 1, 11, 111 }, Strings = new[] { "1", "11", "111" }, @@ -982,6 +1029,7 @@ private static IReadOnlyList CreatePrimitiveArrayEnt Bools = new[] { false }, Enums = new[] { MyEnum.Value2, MyEnum.Value3 }, NullableInts = new int?[] { 1, 11, null }, + NullableStrings = new[] { "1", "11", null } }, new() { @@ -993,6 +1041,7 @@ private static IReadOnlyList CreatePrimitiveArrayEnt Bool = true, Enum = MyEnum.Value1, NullableInt = 20, + NullableString = "20", Ints = new[] { 1, 1, 10, 10, 10, 1, 10 }, Strings = new[] { "1", "10", "10", "1", "1" }, @@ -1007,6 +1056,7 @@ private static IReadOnlyList CreatePrimitiveArrayEnt Bools = new[] { true, false }, Enums = new[] { MyEnum.Value1, MyEnum.Value2 }, NullableInts = new int?[] { 1, 1, 10, 10, null, 1 }, + NullableStrings = new[] { "1", "1", "10", "10", null, "1" } }, new() { @@ -1018,6 +1068,7 @@ private static IReadOnlyList CreatePrimitiveArrayEnt Bool = false, Enum = MyEnum.Value2, NullableInt = null, + NullableString = null, Ints = new[] { 1, 1, 111, 11, 1, 111 }, Strings = new[] { "1", "11", "111", "11" }, @@ -1035,6 +1086,7 @@ private static IReadOnlyList CreatePrimitiveArrayEnt Bools = new[] { false }, Enums = new[] { MyEnum.Value2, MyEnum.Value3 }, NullableInts = new int?[] { null, null }, + NullableStrings = new string?[] { null, null } }, new() { @@ -1046,6 +1098,7 @@ private static IReadOnlyList CreatePrimitiveArrayEnt Bool = false, Enum = MyEnum.Value1, NullableInt = null, + NullableString = null, Ints = Array.Empty(), Strings = Array.Empty(), @@ -1053,6 +1106,7 @@ private static IReadOnlyList CreatePrimitiveArrayEnt Bools = Array.Empty(), Enums = Array.Empty(), NullableInts = Array.Empty(), + NullableStrings = Array.Empty() } }; } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQueryOldSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQueryOldSqlServerTest.cs index a06b20d0242..f3dce36adad 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQueryOldSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQueryOldSqlServerTest.cs @@ -26,7 +26,7 @@ public override async Task Inline_collection_of_ints_Contains(bool async) AssertSql( """ -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE [p].[Int] IN (10, 999) """); @@ -38,7 +38,7 @@ public override async Task Inline_collection_of_nullable_ints_Contains(bool asyn AssertSql( """ -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE [p].[NullableInt] IN (10, 999) """); @@ -50,7 +50,7 @@ public override async Task Inline_collection_of_nullable_ints_Contains_null(bool AssertSql( """ -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE [p].[NullableInt] IS NULL OR [p].[NullableInt] = 999 """); @@ -67,7 +67,7 @@ public override async Task Inline_collection_Count_with_one_value(bool async) AssertSql( """ -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE ( SELECT COUNT(*) @@ -82,7 +82,7 @@ public override async Task Inline_collection_Count_with_two_values(bool async) AssertSql( """ -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE ( SELECT COUNT(*) @@ -97,7 +97,7 @@ public override async Task Inline_collection_Count_with_three_values(bool async) AssertSql( """ -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE ( SELECT COUNT(*) @@ -117,7 +117,7 @@ public override async Task Inline_collection_Contains_with_one_value(bool async) AssertSql( """ -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE [p].[Id] = 2 """); @@ -129,7 +129,7 @@ public override async Task Inline_collection_Contains_with_two_values(bool async AssertSql( """ -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE [p].[Id] IN (2, 999) """); @@ -141,7 +141,7 @@ public override async Task Inline_collection_Contains_with_three_values(bool asy AssertSql( """ -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE [p].[Id] IN (2, 999, 1000) """); @@ -156,7 +156,7 @@ public override async Task Inline_collection_Contains_with_all_parameters(bool a @__i_0='2' @__j_1='999' -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE [p].[Id] IN (@__i_0, @__j_1) """); @@ -170,7 +170,7 @@ public override async Task Inline_collection_Contains_with_constant_and_paramete """ @__j_0='999' -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE [p].[Id] IN (2, @__j_0) """); @@ -184,7 +184,7 @@ public override async Task Inline_collection_Contains_with_mixed_value_types(boo """ @__i_0='11' -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE [p].[Int] IN (999, @__i_0, [p].[Id], [p].[Id] + [p].[Int]) """); @@ -196,7 +196,7 @@ public override async Task Inline_collection_Contains_as_Any_with_predicate(bool AssertSql( """ -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE [p].[Id] IN (2, 999) """); @@ -208,7 +208,7 @@ public override async Task Inline_collection_negated_Contains_as_All(bool async) AssertSql( """ -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE [p].[Id] NOT IN (2, 999) """); @@ -223,7 +223,7 @@ public override async Task Parameter_collection_of_ints_Contains(bool async) AssertSql( """ -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE [p].[Int] IN (10, 999) """); @@ -235,7 +235,7 @@ public override async Task Parameter_collection_of_nullable_ints_Contains_int(bo AssertSql( """ -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE [p].[Int] IN (10, 999) """); @@ -247,31 +247,43 @@ public override async Task Parameter_collection_of_nullable_ints_Contains_nullab AssertSql( """ -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE [p].[NullableInt] IS NULL OR [p].[NullableInt] = 999 """); } - public override async Task Parameter_collection_of_strings_Contains(bool async) + public override async Task Parameter_collection_of_strings_Contains_non_nullable_string(bool async) { - await base.Parameter_collection_of_strings_Contains(async); + await base.Parameter_collection_of_strings_Contains_non_nullable_string(async); AssertSql( """ -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE [p].[String] IN (N'10', N'999') """); } + public override async Task Parameter_collection_of_strings_Contains_nullable_string(bool async) + { + await base.Parameter_collection_of_strings_Contains_nullable_string(async); + + AssertSql( +""" +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE [p].[NullableString] IS NULL OR [p].[NullableString] = N'999' +"""); + } + public override async Task Parameter_collection_of_DateTimes_Contains(bool async) { await base.Parameter_collection_of_DateTimes_Contains(async); AssertSql( """ -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE [p].[DateTime] IN ('2020-01-10T12:30:00.0000000Z', '9999-01-01T00:00:00.0000000Z') """); @@ -283,7 +295,7 @@ public override async Task Parameter_collection_of_bools_Contains(bool async) AssertSql( """ -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE [p].[Bool] = CAST(1 AS bit) """); @@ -295,7 +307,7 @@ public override async Task Parameter_collection_of_enums_Contains(bool async) AssertSql( """ -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE [p].[Enum] IN (0, 3) """); @@ -307,7 +319,7 @@ public override async Task Parameter_collection_null_Contains(bool async) AssertSql( """ -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE 0 = 1 """); @@ -325,6 +337,9 @@ public override Task Column_collection_of_nullable_ints_Contains_null(bool async public override Task Column_collection_of_strings_contains_null(bool async) => AssertTranslationFailed(() => base.Column_collection_of_strings_contains_null(async)); + public override Task Column_collection_of_nullable_strings_contains_null(bool async) + => AssertTranslationFailed(() => base.Column_collection_of_strings_contains_null(async)); + public override Task Column_collection_of_bools_Contains(bool async) => AssertCompatibilityLevelTooLow(() => base.Column_collection_of_bools_Contains(async)); @@ -356,13 +371,19 @@ public override Task Column_collection_index_datetime(bool async) public override Task Column_collection_index_beyond_end(bool async) => AssertCompatibilityLevelTooLow(() => base.Column_collection_index_beyond_end(async)); + public override Task Nullable_reference_column_collection_index_equals_nullable_column(bool async) + => AssertCompatibilityLevelTooLow(() => base.Column_collection_index_beyond_end(async)); + + public override Task Non_nullable_reference_column_collection_index_equals_nullable_column(bool async) + => AssertCompatibilityLevelTooLow(() => base.Column_collection_index_beyond_end(async)); + public override async Task Inline_collection_index_Column(bool async) { await base.Inline_collection_index_Column(async); AssertSql( """ -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE ( SELECT [v].[Value] @@ -437,7 +458,7 @@ public override async Task Column_collection_equality_parameter_collection(bool """ @__ints_0='[1,10]' (Size = 4000) -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE [p].[Ints] = @__ints_0 """); @@ -456,7 +477,7 @@ public override async Task Column_collection_equality_inline_collection(bool asy AssertSql( """ -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE [p].[Ints] = N'[1,10]' """); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQuerySqlServerTest.cs index dd15cb86944..e2ddaa9a82f 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQuerySqlServerTest.cs @@ -3,6 +3,8 @@ namespace Microsoft.EntityFrameworkCore.Query; +#nullable enable + public class PrimitiveCollectionsQuerySqlServerTest : PrimitiveCollectionsQueryTestBase< PrimitiveCollectionsQuerySqlServerTest.PrimitiveCollectionsQuerySqlServerFixture> { @@ -19,7 +21,7 @@ public override async Task Inline_collection_of_ints_Contains(bool async) AssertSql( """ -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE [p].[Int] IN (10, 999) """); @@ -31,7 +33,7 @@ public override async Task Inline_collection_of_nullable_ints_Contains(bool asyn AssertSql( """ -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE [p].[NullableInt] IN (10, 999) """); @@ -43,7 +45,7 @@ public override async Task Inline_collection_of_nullable_ints_Contains_null(bool AssertSql( """ -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE [p].[NullableInt] IS NULL OR [p].[NullableInt] = 999 """); @@ -60,7 +62,7 @@ public override async Task Inline_collection_Count_with_one_value(bool async) AssertSql( """ -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE ( SELECT COUNT(*) @@ -75,7 +77,7 @@ public override async Task Inline_collection_Count_with_two_values(bool async) AssertSql( """ -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE ( SELECT COUNT(*) @@ -90,7 +92,7 @@ public override async Task Inline_collection_Count_with_three_values(bool async) AssertSql( """ -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE ( SELECT COUNT(*) @@ -110,7 +112,7 @@ public override async Task Inline_collection_Contains_with_one_value(bool async) AssertSql( """ -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE [p].[Id] = 2 """); @@ -122,7 +124,7 @@ public override async Task Inline_collection_Contains_with_two_values(bool async AssertSql( """ -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE [p].[Id] IN (2, 999) """); @@ -134,7 +136,7 @@ public override async Task Inline_collection_Contains_with_three_values(bool asy AssertSql( """ -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE [p].[Id] IN (2, 999, 1000) """); @@ -149,7 +151,7 @@ public override async Task Inline_collection_Contains_with_all_parameters(bool a @__i_0='2' @__j_1='999' -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE [p].[Id] IN (@__i_0, @__j_1) """); @@ -163,7 +165,7 @@ public override async Task Inline_collection_Contains_with_constant_and_paramete """ @__j_0='999' -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE [p].[Id] IN (2, @__j_0) """); @@ -177,7 +179,7 @@ public override async Task Inline_collection_Contains_with_mixed_value_types(boo """ @__i_0='11' -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE [p].[Int] IN (999, @__i_0, [p].[Id], [p].[Id] + [p].[Int]) """); @@ -189,7 +191,7 @@ public override async Task Inline_collection_Contains_as_Any_with_predicate(bool AssertSql( """ -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE [p].[Id] IN (2, 999) """); @@ -201,7 +203,7 @@ public override async Task Inline_collection_negated_Contains_as_All(bool async) AssertSql( """ -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE [p].[Id] NOT IN (2, 999) """); @@ -215,7 +217,7 @@ public override async Task Parameter_collection_Count(bool async) """ @__ids_0='[2,999]' (Size = 4000) -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE ( SELECT COUNT(*) @@ -232,7 +234,7 @@ public override async Task Parameter_collection_of_ints_Contains(bool async) """ @__ints_0='[10,999]' (Size = 4000) -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE [p].[Int] IN ( SELECT [i].[value] @@ -249,7 +251,7 @@ public override async Task Parameter_collection_of_nullable_ints_Contains_int(bo """ @__nullableInts_0='[10,999]' (Size = 4000) -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE [p].[Int] IN ( SELECT [n].[value] @@ -266,7 +268,7 @@ public override async Task Parameter_collection_of_nullable_ints_Contains_nullab """ @__nullableInts_0='[null,999]' (Size = 4000) -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE EXISTS ( SELECT 1 @@ -275,20 +277,37 @@ FROM OPENJSON(@__nullableInts_0) WITH ([value] int '$') AS [n] """); } - public override async Task Parameter_collection_of_strings_Contains(bool async) + public override async Task Parameter_collection_of_strings_Contains_non_nullable_string(bool async) { - await base.Parameter_collection_of_strings_Contains(async); + await base.Parameter_collection_of_strings_Contains_non_nullable_string(async); AssertSql( """ @__strings_0='["10","999"]' (Size = 4000) -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE [p].[String] IN ( + SELECT [s].[value] + FROM OPENJSON(@__strings_0) WITH ([value] nvarchar(max) '$') AS [s] +) +"""); + } + + public override async Task Parameter_collection_of_strings_Contains_nullable_string(bool async) + { + await base.Parameter_collection_of_strings_Contains_nullable_string(async); + + AssertSql( + """ +@__strings_0='["999",null]' (Size = 4000) + +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE EXISTS ( SELECT 1 FROM OPENJSON(@__strings_0) WITH ([value] nvarchar(max) '$') AS [s] - WHERE [s].[value] = [p].[String] OR ([s].[value] IS NULL AND [p].[String] IS NULL)) + WHERE [s].[value] = [p].[NullableString] OR ([s].[value] IS NULL AND [p].[NullableString] IS NULL)) """); } @@ -300,7 +319,7 @@ public override async Task Parameter_collection_of_DateTimes_Contains(bool async """ @__dateTimes_0='["2020-01-10T12:30:00Z","9999-01-01T00:00:00Z"]' (Size = 4000) -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE [p].[DateTime] IN ( SELECT [d].[value] @@ -317,7 +336,7 @@ public override async Task Parameter_collection_of_bools_Contains(bool async) """ @__bools_0='[true]' (Size = 4000) -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE [p].[Bool] IN ( SELECT [b].[value] @@ -334,7 +353,7 @@ public override async Task Parameter_collection_of_enums_Contains(bool async) """ @__enums_0='[0,3]' (Size = 4000) -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE [p].[Enum] IN ( SELECT [e].[value] @@ -349,7 +368,7 @@ public override async Task Parameter_collection_null_Contains(bool async) AssertSql( """ -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE [p].[Int] IN ( SELECT [i].[value] @@ -364,7 +383,7 @@ public override async Task Column_collection_of_ints_Contains(bool async) AssertSql( """ -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE 10 IN ( SELECT [i].[value] @@ -379,7 +398,7 @@ public override async Task Column_collection_of_nullable_ints_Contains(bool asyn AssertSql( """ -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE 10 IN ( SELECT [n].[value] @@ -394,7 +413,7 @@ public override async Task Column_collection_of_nullable_ints_Contains_null(bool AssertSql( """ -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE EXISTS ( SELECT 1 @@ -409,12 +428,24 @@ public override async Task Column_collection_of_strings_contains_null(bool async AssertSql( """ -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE 0 = 1 +"""); + } + + public override async Task Column_collection_of_nullable_strings_contains_null(bool async) + { + await base.Column_collection_of_nullable_strings_contains_null(async); + + AssertSql( +""" +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE EXISTS ( SELECT 1 - FROM OPENJSON([p].[Strings]) WITH ([value] nvarchar(max) '$') AS [s] - WHERE [s].[value] IS NULL) + FROM OPENJSON([p].[NullableStrings]) WITH ([value] nvarchar(max) '$') AS [n] + WHERE [n].[value] IS NULL) """); } @@ -424,7 +455,7 @@ public override async Task Column_collection_of_bools_Contains(bool async) AssertSql( """ -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE CAST(1 AS bit) IN ( SELECT [b].[value] @@ -450,7 +481,7 @@ public override async Task Column_collection_Count_method(bool async) AssertSql( """ -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE ( SELECT COUNT(*) @@ -464,7 +495,7 @@ public override async Task Column_collection_Length(bool async) AssertSql( """ -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE ( SELECT COUNT(*) @@ -478,7 +509,7 @@ public override async Task Column_collection_index_int(bool async) AssertSql( """ -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE CAST(JSON_VALUE([p].[Ints], '$[1]') AS int) = 10 """); @@ -490,7 +521,7 @@ public override async Task Column_collection_index_string(bool async) AssertSql( """ -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE JSON_VALUE([p].[Strings], '$[1]') = N'10' """); @@ -502,7 +533,7 @@ public override async Task Column_collection_index_datetime(bool async) AssertSql( """ -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE CAST(JSON_VALUE([p].[DateTimes], '$[1]') AS datetime2) = '2020-01-10T12:30:00.0000000Z' """); @@ -514,19 +545,45 @@ public override async Task Column_collection_index_beyond_end(bool async) AssertSql( """ -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE CAST(JSON_VALUE([p].[Ints], '$[999]') AS int) = 10 """); } + public override async Task Nullable_reference_column_collection_index_equals_nullable_column(bool async) + { + await base.Nullable_reference_column_collection_index_equals_nullable_column(async); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE JSON_VALUE([p].[NullableStrings], '$[2]') = [p].[NullableString] OR (JSON_VALUE([p].[NullableStrings], '$[2]') IS NULL AND [p].[NullableString] IS NULL) +"""); + } + + public override async Task Non_nullable_reference_column_collection_index_equals_nullable_column(bool async) + { + await base.Non_nullable_reference_column_collection_index_equals_nullable_column(async); + + AssertSql( +""" +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE EXISTS ( + SELECT 1 + FROM OPENJSON([p].[Strings]) WITH ([value] nvarchar(max) '$') AS [s]) AND JSON_VALUE([p].[Strings], '$[1]') = [p].[NullableString] +"""); + } + public override async Task Inline_collection_index_Column(bool async) { await base.Inline_collection_index_Column(async); AssertSql( """ -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE ( SELECT [v].[Value] @@ -545,7 +602,7 @@ public override async Task Parameter_collection_index_Column_equal_Column(bool a """ @__ints_0='[0,2,3]' (Size = 4000) -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE CAST(JSON_VALUE(@__ints_0, '$[' + CAST([p].[Int] AS nvarchar(max)) + ']') AS int) = [p].[Int] """); @@ -560,7 +617,7 @@ public override async Task Parameter_collection_index_Column_equal_constant(bool """ @__ints_0='[1,2,3]' (Size = 4000) -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE CAST(JSON_VALUE(@__ints_0, '$[' + CAST([p].[Int] AS nvarchar(max)) + ']') AS int) = 1 """); @@ -572,7 +629,7 @@ public override async Task Column_collection_ElementAt(bool async) AssertSql( """ -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE CAST(JSON_VALUE([p].[Ints], '$[1]') AS int) = 10 """); @@ -584,7 +641,7 @@ public override async Task Column_collection_Skip(bool async) AssertSql( """ -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE ( SELECT COUNT(*) @@ -603,7 +660,7 @@ public override async Task Column_collection_Take(bool async) AssertSql( """ -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE 11 IN ( SELECT TOP(2) CAST([i].[value] AS int) AS [value] @@ -619,7 +676,7 @@ public override async Task Column_collection_Skip_Take(bool async) AssertSql( """ -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE 11 IN ( SELECT CAST([i].[value] AS int) AS [value] @@ -636,7 +693,7 @@ public override async Task Column_collection_OrderByDescending_ElementAt(bool as AssertSql( """ -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE ( SELECT [i].[value] @@ -652,7 +709,7 @@ public override async Task Column_collection_Any(bool async) AssertSql( """ -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE EXISTS ( SELECT 1 @@ -665,8 +722,8 @@ public override async Task Column_collection_Distinct(bool async) await base.Column_collection_Distinct(async); AssertSql( -""" -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE ( SELECT COUNT(*) @@ -697,7 +754,7 @@ public override async Task Column_collection_Join_parameter_collection(bool asyn """ @__ints_0='[11,111]' (Size = 4000) -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE ( SELECT COUNT(*) @@ -712,7 +769,7 @@ public override async Task Inline_collection_Join_ordered_column_collection(bool AssertSql( """ -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE ( SELECT COUNT(*) @@ -729,7 +786,7 @@ public override async Task Parameter_collection_Concat_column_collection(bool as """ @__ints_0='[11,111]' (Size = 4000) -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE ( SELECT COUNT(*) @@ -751,7 +808,7 @@ public override async Task Column_collection_Union_parameter_collection(bool asy """ @__ints_0='[11,111]' (Size = 4000) -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE ( SELECT COUNT(*) @@ -771,7 +828,7 @@ public override async Task Column_collection_Intersect_inline_collection(bool as AssertSql( """ -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE ( SELECT COUNT(*) @@ -791,7 +848,7 @@ public override async Task Inline_collection_Except_column_collection(bool async AssertSql( """ -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE ( SELECT COUNT(*) @@ -814,7 +871,7 @@ public override async Task Column_collection_equality_parameter_collection(bool """ @__ints_0='[1,10]' (Size = 4000) -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE [p].[Ints] = @__ints_0 """); @@ -833,7 +890,7 @@ public override async Task Column_collection_equality_inline_collection(bool asy AssertSql( """ -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE [p].[Ints] = N'[1,10]' """); @@ -854,7 +911,7 @@ public override async Task Parameter_collection_in_subquery_Union_column_collect """ @__ints='[10,111]' (Size = 4000) -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE ( SELECT COUNT(*) @@ -878,10 +935,10 @@ public override async Task Parameter_collection_in_subquery_Union_column_collect await base.Parameter_collection_in_subquery_Union_column_collection(async); AssertSql( -""" + """ @__Skip_0='[111]' (Size = 4000) -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE ( SELECT COUNT(*) @@ -900,10 +957,10 @@ public override async Task Parameter_collection_in_subquery_Union_column_collect await base.Parameter_collection_in_subquery_Union_column_collection_nested(async); AssertSql( -""" + """ @__Skip_0='[111]' (Size = 4000) -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE ( SELECT COUNT(*) @@ -975,7 +1032,7 @@ public override async Task Column_collection_in_subquery_Union_parameter_collect """ @__ints_0='[10,111]' (Size = 4000) -SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings] +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings] FROM [PrimitiveCollectionsEntity] AS [p] WHERE ( SELECT COUNT(*) @@ -999,7 +1056,7 @@ public override async Task Project_collection_of_ints_simple(bool async) await base.Project_collection_of_ints_simple(async); AssertSql( -""" + """ SELECT [p].[Ints] FROM [PrimitiveCollectionsEntity] AS [p] ORDER BY [p].[Id] @@ -1011,7 +1068,7 @@ public override async Task Project_collection_of_ints_ordered(bool async) await base.Project_collection_of_ints_ordered(async); AssertSql( -""" + """ SELECT [p].[Id], CAST([i].[value] AS int) AS [value], [i].[key] FROM [PrimitiveCollectionsEntity] AS [p] OUTER APPLY OPENJSON([p].[Ints]) AS [i] @@ -1024,7 +1081,7 @@ public override async Task Project_collection_of_datetimes_filtered(bool async) await base.Project_collection_of_datetimes_filtered(async); AssertSql( -""" + """ SELECT [p].[Id], [t].[value], [t].[key] FROM [PrimitiveCollectionsEntity] AS [p] OUTER APPLY ( @@ -1041,7 +1098,7 @@ public override async Task Project_collection_of_ints_with_paging(bool async) await base.Project_collection_of_ints_with_paging(async); AssertSql( -""" + """ SELECT [p].[Id], [t].[value], [t].[key] FROM [PrimitiveCollectionsEntity] AS [p] OUTER APPLY ( @@ -1058,7 +1115,7 @@ public override async Task Project_collection_of_ints_with_paging2(bool async) await base.Project_collection_of_ints_with_paging2(async); AssertSql( -""" + """ SELECT [p].[Id], [t].[value], [t].[key] FROM [PrimitiveCollectionsEntity] AS [p] OUTER APPLY ( @@ -1076,7 +1133,7 @@ public override async Task Project_collection_of_ints_with_paging3(bool async) await base.Project_collection_of_ints_with_paging3(async); AssertSql( -""" + """ SELECT [p].[Id], [t].[value], [t].[key] FROM [PrimitiveCollectionsEntity] AS [p] OUTER APPLY ( @@ -1094,7 +1151,7 @@ public override async Task Project_collection_of_ints_with_distinct(bool async) await base.Project_collection_of_ints_with_distinct(async); AssertSql( -""" + """ SELECT [p].[Id], [t].[value] FROM [PrimitiveCollectionsEntity] AS [p] OUTER APPLY ( @@ -1117,7 +1174,7 @@ public override async Task Project_empty_collection_of_nullables_and_collection_ await base.Project_empty_collection_of_nullables_and_collection_only_containing_nulls(async); AssertSql( -""" + """ SELECT [p].[Id], [t].[value], [t].[key], [t0].[value], [t0].[key] FROM [PrimitiveCollectionsEntity] AS [p] OUTER APPLY ( @@ -1139,7 +1196,7 @@ public override async Task Project_multiple_collections(bool async) await base.Project_multiple_collections(async); AssertSql( -""" + """ SELECT [p].[Id], CAST([i].[value] AS int) AS [value], [i].[key], CAST([i0].[value] AS int) AS [value], [i0].[key], [t].[value], [t].[key], [t0].[value], [t0].[key] FROM [PrimitiveCollectionsEntity] AS [p] OUTER APPLY OPENJSON([p].[Ints]) AS [i] @@ -1163,7 +1220,7 @@ public override async Task Project_primitive_collections_element(bool async) await base.Project_primitive_collections_element(async); AssertSql( -""" + """ SELECT CAST(JSON_VALUE([p].[Ints], '$[0]') AS int) AS [Indexer], CAST(JSON_VALUE([p].[DateTimes], '$[0]') AS datetime2) AS [EnumerableElementAt], JSON_VALUE([p].[Strings], '$[1]') AS [QueryableElementAt] FROM [PrimitiveCollectionsEntity] AS [p] WHERE [p].[Id] < 4 diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/PrimitiveCollectionsQuerySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/PrimitiveCollectionsQuerySqliteTest.cs index 37b34f924f1..e5e028e8ac3 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/PrimitiveCollectionsQuerySqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/PrimitiveCollectionsQuerySqliteTest.cs @@ -22,7 +22,7 @@ public override async Task Inline_collection_of_ints_Contains(bool async) AssertSql( """ -SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."String", "p"."Strings" +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."String", "p"."Strings" FROM "PrimitiveCollectionsEntity" AS "p" WHERE "p"."Int" IN (10, 999) """); @@ -34,7 +34,7 @@ public override async Task Inline_collection_of_nullable_ints_Contains(bool asyn AssertSql( """ -SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."String", "p"."Strings" +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."String", "p"."Strings" FROM "PrimitiveCollectionsEntity" AS "p" WHERE "p"."NullableInt" IN (10, 999) """); @@ -46,7 +46,7 @@ public override async Task Inline_collection_of_nullable_ints_Contains_null(bool AssertSql( """ -SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."String", "p"."Strings" +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."String", "p"."Strings" FROM "PrimitiveCollectionsEntity" AS "p" WHERE "p"."NullableInt" IS NULL OR "p"."NullableInt" = 999 """); @@ -63,7 +63,7 @@ public override async Task Inline_collection_Count_with_one_value(bool async) AssertSql( """ -SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."String", "p"."Strings" +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."String", "p"."Strings" FROM "PrimitiveCollectionsEntity" AS "p" WHERE ( SELECT COUNT(*) @@ -78,7 +78,7 @@ public override async Task Inline_collection_Count_with_two_values(bool async) AssertSql( """ -SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."String", "p"."Strings" +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."String", "p"."Strings" FROM "PrimitiveCollectionsEntity" AS "p" WHERE ( SELECT COUNT(*) @@ -93,7 +93,7 @@ public override async Task Inline_collection_Count_with_three_values(bool async) AssertSql( """ -SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."String", "p"."Strings" +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."String", "p"."Strings" FROM "PrimitiveCollectionsEntity" AS "p" WHERE ( SELECT COUNT(*) @@ -113,7 +113,7 @@ public override async Task Inline_collection_Contains_with_one_value(bool async) AssertSql( """ -SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."String", "p"."Strings" +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."String", "p"."Strings" FROM "PrimitiveCollectionsEntity" AS "p" WHERE "p"."Id" = 2 """); @@ -125,7 +125,7 @@ public override async Task Inline_collection_Contains_with_two_values(bool async AssertSql( """ -SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."String", "p"."Strings" +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."String", "p"."Strings" FROM "PrimitiveCollectionsEntity" AS "p" WHERE "p"."Id" IN (2, 999) """); @@ -137,7 +137,7 @@ public override async Task Inline_collection_Contains_with_three_values(bool asy AssertSql( """ -SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."String", "p"."Strings" +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."String", "p"."Strings" FROM "PrimitiveCollectionsEntity" AS "p" WHERE "p"."Id" IN (2, 999, 1000) """); @@ -152,7 +152,7 @@ public override async Task Inline_collection_Contains_with_all_parameters(bool a @__i_0='2' @__j_1='999' -SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."String", "p"."Strings" +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."String", "p"."Strings" FROM "PrimitiveCollectionsEntity" AS "p" WHERE "p"."Id" IN (@__i_0, @__j_1) """); @@ -166,7 +166,7 @@ public override async Task Inline_collection_Contains_with_constant_and_paramete """ @__j_0='999' -SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."String", "p"."Strings" +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."String", "p"."Strings" FROM "PrimitiveCollectionsEntity" AS "p" WHERE "p"."Id" IN (2, @__j_0) """); @@ -180,7 +180,7 @@ public override async Task Inline_collection_Contains_with_mixed_value_types(boo """ @__i_0='11' -SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."String", "p"."Strings" +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."String", "p"."Strings" FROM "PrimitiveCollectionsEntity" AS "p" WHERE "p"."Int" IN (999, @__i_0, "p"."Id", "p"."Id" + "p"."Int") """); @@ -192,7 +192,7 @@ public override async Task Inline_collection_Contains_as_Any_with_predicate(bool AssertSql( """ -SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."String", "p"."Strings" +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."String", "p"."Strings" FROM "PrimitiveCollectionsEntity" AS "p" WHERE "p"."Id" IN (2, 999) """); @@ -204,7 +204,7 @@ public override async Task Inline_collection_negated_Contains_as_All(bool async) AssertSql( """ -SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."String", "p"."Strings" +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."String", "p"."Strings" FROM "PrimitiveCollectionsEntity" AS "p" WHERE "p"."Id" NOT IN (2, 999) """); @@ -218,7 +218,7 @@ public override async Task Parameter_collection_Count(bool async) """ @__ids_0='[2,999]' (Size = 7) -SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."String", "p"."Strings" +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."String", "p"."Strings" FROM "PrimitiveCollectionsEntity" AS "p" WHERE ( SELECT COUNT(*) @@ -235,7 +235,7 @@ public override async Task Parameter_collection_of_ints_Contains(bool async) """ @__ints_0='[10,999]' (Size = 8) -SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."String", "p"."Strings" +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."String", "p"."Strings" FROM "PrimitiveCollectionsEntity" AS "p" WHERE "p"."Int" IN ( SELECT "i"."value" @@ -252,7 +252,7 @@ public override async Task Parameter_collection_of_nullable_ints_Contains_int(bo """ @__nullableInts_0='[10,999]' (Size = 8) -SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."String", "p"."Strings" +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."String", "p"."Strings" FROM "PrimitiveCollectionsEntity" AS "p" WHERE "p"."Int" IN ( SELECT "n"."value" @@ -269,7 +269,7 @@ public override async Task Parameter_collection_of_nullable_ints_Contains_nullab """ @__nullableInts_0='[null,999]' (Size = 10) -SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."String", "p"."Strings" +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."String", "p"."Strings" FROM "PrimitiveCollectionsEntity" AS "p" WHERE EXISTS ( SELECT 1 @@ -278,21 +278,39 @@ FROM json_each(@__nullableInts_0) AS "n" """); } - public override async Task Parameter_collection_of_strings_Contains(bool async) + public override async Task Parameter_collection_of_strings_Contains_non_nullable_string(bool async) { - await base.Parameter_collection_of_strings_Contains(async); + await base.Parameter_collection_of_strings_Contains_non_nullable_string(async); AssertSql( """ @__strings_0='["10","999"]' (Size = 12) -SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."String", "p"."Strings" +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."String", "p"."Strings" +FROM "PrimitiveCollectionsEntity" AS "p" +WHERE "p"."String" IN ( + SELECT "s"."value" + FROM json_each(@__strings_0) AS "s" +) +"""); + } + + public override async Task Parameter_collection_of_strings_Contains_nullable_string(bool async) + { + await base.Parameter_collection_of_strings_Contains_nullable_string(async); + + AssertSql( + """ +@__strings_0='["999",null]' (Size = 12) + +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."String", "p"."Strings" FROM "PrimitiveCollectionsEntity" AS "p" WHERE EXISTS ( SELECT 1 FROM json_each(@__strings_0) AS "s" - WHERE "s"."value" = "p"."String" OR ("s"."value" IS NULL AND "p"."String" IS NULL)) + WHERE "s"."value" = "p"."NullableString" OR ("s"."value" IS NULL AND "p"."NullableString" IS NULL)) """); + } public override async Task Parameter_collection_of_DateTimes_Contains(bool async) @@ -303,7 +321,7 @@ public override async Task Parameter_collection_of_DateTimes_Contains(bool async """ @__dateTimes_0='["2020-01-10T12:30:00Z","9999-01-01T00:00:00Z"]' (Size = 47) -SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."String", "p"."Strings" +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."String", "p"."Strings" FROM "PrimitiveCollectionsEntity" AS "p" WHERE "p"."DateTime" IN ( SELECT rtrim(rtrim(strftime('%Y-%m-%d %H:%M:%f', "d"."value"), '0'), '.') AS "value" @@ -320,7 +338,7 @@ public override async Task Parameter_collection_of_bools_Contains(bool async) """ @__bools_0='[true]' (Size = 6) -SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."String", "p"."Strings" +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."String", "p"."Strings" FROM "PrimitiveCollectionsEntity" AS "p" WHERE "p"."Bool" IN ( SELECT "b"."value" @@ -337,7 +355,7 @@ public override async Task Parameter_collection_of_enums_Contains(bool async) """ @__enums_0='[0,3]' (Size = 5) -SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."String", "p"."Strings" +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."String", "p"."Strings" FROM "PrimitiveCollectionsEntity" AS "p" WHERE "p"."Enum" IN ( SELECT "e"."value" @@ -352,7 +370,7 @@ public override async Task Parameter_collection_null_Contains(bool async) AssertSql( """ -SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."String", "p"."Strings" +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."String", "p"."Strings" FROM "PrimitiveCollectionsEntity" AS "p" WHERE "p"."Int" IN ( SELECT "i"."value" @@ -367,7 +385,7 @@ public override async Task Column_collection_of_ints_Contains(bool async) AssertSql( """ -SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."String", "p"."Strings" +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."String", "p"."Strings" FROM "PrimitiveCollectionsEntity" AS "p" WHERE 10 IN ( SELECT "i"."value" @@ -382,7 +400,7 @@ public override async Task Column_collection_of_nullable_ints_Contains(bool asyn AssertSql( """ -SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."String", "p"."Strings" +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."String", "p"."Strings" FROM "PrimitiveCollectionsEntity" AS "p" WHERE 10 IN ( SELECT "n"."value" @@ -397,7 +415,7 @@ public override async Task Column_collection_of_nullable_ints_Contains_null(bool AssertSql( """ -SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."String", "p"."Strings" +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."String", "p"."Strings" FROM "PrimitiveCollectionsEntity" AS "p" WHERE EXISTS ( SELECT 1 @@ -412,12 +430,24 @@ public override async Task Column_collection_of_strings_contains_null(bool async AssertSql( """ -SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."String", "p"."Strings" +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."String", "p"."Strings" +FROM "PrimitiveCollectionsEntity" AS "p" +WHERE 0 +"""); + } + + public override async Task Column_collection_of_nullable_strings_contains_null(bool async) + { + await base.Column_collection_of_nullable_strings_contains_null(async); + + AssertSql( + """ +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."String", "p"."Strings" FROM "PrimitiveCollectionsEntity" AS "p" WHERE EXISTS ( SELECT 1 - FROM json_each("p"."Strings") AS "s" - WHERE "s"."value" IS NULL) + FROM json_each("p"."NullableStrings") AS "n" + WHERE "n"."value" IS NULL) """); } @@ -427,7 +457,7 @@ public override async Task Column_collection_of_bools_Contains(bool async) AssertSql( """ -SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."String", "p"."Strings" +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."String", "p"."Strings" FROM "PrimitiveCollectionsEntity" AS "p" WHERE 1 IN ( SELECT "b"."value" @@ -442,7 +472,7 @@ public override async Task Column_collection_Count_method(bool async) AssertSql( """ -SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."String", "p"."Strings" +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."String", "p"."Strings" FROM "PrimitiveCollectionsEntity" AS "p" WHERE json_array_length("p"."Ints") = 2 """); @@ -454,7 +484,7 @@ public override async Task Column_collection_Length(bool async) AssertSql( """ -SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."String", "p"."Strings" +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."String", "p"."Strings" FROM "PrimitiveCollectionsEntity" AS "p" WHERE json_array_length("p"."Ints") = 2 """); @@ -466,7 +496,7 @@ public override async Task Column_collection_index_int(bool async) AssertSql( """ -SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."String", "p"."Strings" +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."String", "p"."Strings" FROM "PrimitiveCollectionsEntity" AS "p" WHERE "p"."Ints" ->> 1 = 10 """); @@ -478,7 +508,7 @@ public override async Task Column_collection_index_string(bool async) AssertSql( """ -SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."String", "p"."Strings" +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."String", "p"."Strings" FROM "PrimitiveCollectionsEntity" AS "p" WHERE "p"."Strings" ->> 1 = '10' """); @@ -490,7 +520,7 @@ public override async Task Column_collection_index_datetime(bool async) AssertSql( """ -SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."String", "p"."Strings" +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."String", "p"."Strings" FROM "PrimitiveCollectionsEntity" AS "p" WHERE rtrim(rtrim(strftime('%Y-%m-%d %H:%M:%f', "p"."DateTimes" ->> 1), '0'), '.') = '2020-01-10 12:30:00' """); @@ -502,12 +532,36 @@ public override async Task Column_collection_index_beyond_end(bool async) AssertSql( """ -SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."String", "p"."Strings" +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."String", "p"."Strings" FROM "PrimitiveCollectionsEntity" AS "p" WHERE "p"."Ints" ->> 999 = 10 """); } + public override async Task Nullable_reference_column_collection_index_equals_nullable_column(bool async) + { + await base.Nullable_reference_column_collection_index_equals_nullable_column(async); + + AssertSql( + """ +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."String", "p"."Strings" +FROM "PrimitiveCollectionsEntity" AS "p" +WHERE "p"."NullableStrings" ->> 2 = "p"."NullableString" OR ("p"."NullableStrings" ->> 2 IS NULL AND "p"."NullableString" IS NULL) +"""); + } + + public override async Task Non_nullable_reference_column_collection_index_equals_nullable_column(bool async) + { + await base.Non_nullable_reference_column_collection_index_equals_nullable_column(async); + + AssertSql( + """ +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."String", "p"."Strings" +FROM "PrimitiveCollectionsEntity" AS "p" +WHERE json_array_length("p"."Strings") > 0 AND "p"."Strings" ->> 1 = "p"."NullableString" +"""); + } + public override async Task Inline_collection_index_Column(bool async) { // SQLite doesn't support correlated subqueries where the outer column is used as the LIMIT/OFFSET (see OFFSET "p"."Int" below) @@ -515,7 +569,7 @@ public override async Task Inline_collection_index_Column(bool async) AssertSql( """ -SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."String", "p"."Strings" +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."String", "p"."Strings" FROM "PrimitiveCollectionsEntity" AS "p" WHERE ( SELECT "v"."Value" @@ -533,7 +587,7 @@ public override async Task Parameter_collection_index_Column_equal_Column(bool a """ @__ints_0='[0,2,3]' (Size = 7) -SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."String", "p"."Strings" +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."String", "p"."Strings" FROM "PrimitiveCollectionsEntity" AS "p" WHERE @__ints_0 ->> "p"."Int" = "p"."Int" """); @@ -547,7 +601,7 @@ public override async Task Parameter_collection_index_Column_equal_constant(bool """ @__ints_0='[1,2,3]' (Size = 7) -SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."String", "p"."Strings" +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."String", "p"."Strings" FROM "PrimitiveCollectionsEntity" AS "p" WHERE @__ints_0 ->> "p"."Int" = 1 """); @@ -559,7 +613,7 @@ public override async Task Column_collection_ElementAt(bool async) AssertSql( """ -SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."String", "p"."Strings" +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."String", "p"."Strings" FROM "PrimitiveCollectionsEntity" AS "p" WHERE "p"."Ints" ->> 1 = 10 """); @@ -571,7 +625,7 @@ public override async Task Column_collection_Skip(bool async) AssertSql( """ -SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."String", "p"."Strings" +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."String", "p"."Strings" FROM "PrimitiveCollectionsEntity" AS "p" WHERE ( SELECT COUNT(*) @@ -590,7 +644,7 @@ public override async Task Column_collection_Take(bool async) AssertSql( """ -SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."String", "p"."Strings" +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."String", "p"."Strings" FROM "PrimitiveCollectionsEntity" AS "p" WHERE 11 IN ( SELECT "i"."value" @@ -607,7 +661,7 @@ public override async Task Column_collection_Skip_Take(bool async) AssertSql( """ -SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."String", "p"."Strings" +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."String", "p"."Strings" FROM "PrimitiveCollectionsEntity" AS "p" WHERE 11 IN ( SELECT "i"."value" @@ -624,7 +678,7 @@ public override async Task Column_collection_OrderByDescending_ElementAt(bool as AssertSql( """ -SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."String", "p"."Strings" +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."String", "p"."Strings" FROM "PrimitiveCollectionsEntity" AS "p" WHERE ( SELECT "i"."value" @@ -640,7 +694,7 @@ public override async Task Column_collection_Any(bool async) AssertSql( """ -SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."String", "p"."Strings" +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."String", "p"."Strings" FROM "PrimitiveCollectionsEntity" AS "p" WHERE json_array_length("p"."Ints") > 0 """); @@ -651,8 +705,8 @@ public override async Task Column_collection_Distinct(bool async) await base.Column_collection_Distinct(async); AssertSql( -""" -SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."String", "p"."Strings" + """ +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."String", "p"."Strings" FROM "PrimitiveCollectionsEntity" AS "p" WHERE ( SELECT COUNT(*) @@ -683,7 +737,7 @@ public override async Task Column_collection_Join_parameter_collection(bool asyn """ @__ints_0='[11,111]' (Size = 8) -SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."String", "p"."Strings" +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."String", "p"."Strings" FROM "PrimitiveCollectionsEntity" AS "p" WHERE ( SELECT COUNT(*) @@ -698,7 +752,7 @@ public override async Task Inline_collection_Join_ordered_column_collection(bool AssertSql( """ -SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."String", "p"."Strings" +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."String", "p"."Strings" FROM "PrimitiveCollectionsEntity" AS "p" WHERE ( SELECT COUNT(*) @@ -715,7 +769,7 @@ public override async Task Parameter_collection_Concat_column_collection(bool as """ @__ints_0='[11,111]' (Size = 8) -SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."String", "p"."Strings" +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."String", "p"."Strings" FROM "PrimitiveCollectionsEntity" AS "p" WHERE ( SELECT COUNT(*) @@ -737,7 +791,7 @@ public override async Task Column_collection_Union_parameter_collection(bool asy """ @__ints_0='[11,111]' (Size = 8) -SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."String", "p"."Strings" +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."String", "p"."Strings" FROM "PrimitiveCollectionsEntity" AS "p" WHERE ( SELECT COUNT(*) @@ -757,7 +811,7 @@ public override async Task Column_collection_Intersect_inline_collection(bool as AssertSql( """ -SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."String", "p"."Strings" +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."String", "p"."Strings" FROM "PrimitiveCollectionsEntity" AS "p" WHERE ( SELECT COUNT(*) @@ -776,7 +830,7 @@ public override async Task Inline_collection_Except_column_collection(bool async AssertSql( """ -SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."String", "p"."Strings" +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."String", "p"."Strings" FROM "PrimitiveCollectionsEntity" AS "p" WHERE ( SELECT COUNT(*) @@ -798,7 +852,7 @@ public override async Task Column_collection_equality_parameter_collection(bool """ @__ints_0='[1,10]' (Size = 6) -SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."String", "p"."Strings" +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."String", "p"."Strings" FROM "PrimitiveCollectionsEntity" AS "p" WHERE "p"."Ints" = @__ints_0 """); @@ -817,7 +871,7 @@ public override async Task Column_collection_equality_inline_collection(bool asy AssertSql( """ -SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."String", "p"."Strings" +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."String", "p"."Strings" FROM "PrimitiveCollectionsEntity" AS "p" WHERE "p"."Ints" = '[1,10]' """); @@ -835,7 +889,7 @@ public override async Task Parameter_collection_in_subquery_Count_as_compiled_qu await base.Parameter_collection_in_subquery_Count_as_compiled_query(async); AssertSql( -""" + """ @__ints='[10,111]' (Size = 8) SELECT COUNT(*) @@ -865,10 +919,10 @@ public override async Task Parameter_collection_in_subquery_Union_column_collect await base.Parameter_collection_in_subquery_Union_column_collection_as_compiled_query(async); AssertSql( -""" + """ @__ints='[10,111]' (Size = 8) -SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."String", "p"."Strings" +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."String", "p"."Strings" FROM "PrimitiveCollectionsEntity" AS "p" WHERE ( SELECT COUNT(*) @@ -892,10 +946,10 @@ public override async Task Parameter_collection_in_subquery_Union_column_collect await base.Parameter_collection_in_subquery_Union_column_collection(async); AssertSql( -""" + """ @__Skip_0='[111]' (Size = 5) -SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."String", "p"."Strings" +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."String", "p"."Strings" FROM "PrimitiveCollectionsEntity" AS "p" WHERE ( SELECT COUNT(*) @@ -914,10 +968,10 @@ public override async Task Parameter_collection_in_subquery_Union_column_collect await base.Parameter_collection_in_subquery_Union_column_collection_nested(async); AssertSql( -""" + """ @__Skip_0='[111]' (Size = 5) -SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."String", "p"."Strings" +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."String", "p"."Strings" FROM "PrimitiveCollectionsEntity" AS "p" WHERE ( SELECT COUNT(*) @@ -959,7 +1013,7 @@ public override async Task Column_collection_in_subquery_Union_parameter_collect """ @__ints_0='[10,111]' (Size = 8) -SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."String", "p"."Strings" +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."String", "p"."Strings" FROM "PrimitiveCollectionsEntity" AS "p" WHERE ( SELECT COUNT(*) @@ -983,7 +1037,7 @@ public override async Task Project_collection_of_ints_simple(bool async) await base.Project_collection_of_ints_simple(async); AssertSql( -""" + """ SELECT "p"."Ints" FROM "PrimitiveCollectionsEntity" AS "p" ORDER BY "p"."Id" @@ -1043,7 +1097,7 @@ public override async Task Project_primitive_collections_element(bool async) await base.Project_primitive_collections_element(async); AssertSql( -""" + """ SELECT "p"."Ints" ->> 0 AS "Indexer", rtrim(rtrim(strftime('%Y-%m-%d %H:%M:%f', "p"."DateTimes" ->> 0), '0'), '.') AS "EnumerableElementAt", "p"."Strings" ->> 1 AS "QueryableElementAt" FROM "PrimitiveCollectionsEntity" AS "p" WHERE "p"."Id" < 4 diff --git a/test/EFCore.Tests/Metadata/Conventions/NonNullableReferencePropertyConventionTest.cs b/test/EFCore.Tests/Metadata/Conventions/NonNullableReferencePropertyConventionTest.cs index e14810a40c1..718dc70294f 100644 --- a/test/EFCore.Tests/Metadata/Conventions/NonNullableReferencePropertyConventionTest.cs +++ b/test/EFCore.Tests/Metadata/Conventions/NonNullableReferencePropertyConventionTest.cs @@ -87,6 +87,25 @@ public void Dictionary_indexer_is_not_configured_as_non_nullable() Assert.True(entityTypeBuilder.Property("b").Metadata.IsNullable); } + [ConditionalFact] + public void Primitive_collection_with_non_nullable_element() + { + var modelBuilder = CreateModelBuilder(); + var entityTypeBuilder = modelBuilder.Entity(); + + Assert.False( + entityTypeBuilder.PrimitiveCollection(a => a.PrimitiveCollectionWithNonNullableElement).ElementType().Metadata.IsNullable); + } + + [ConditionalFact] + public void Primitive_collection_with_nullable_element() + { + var modelBuilder = CreateModelBuilder(); + var entityTypeBuilder = modelBuilder.Entity(); + + Assert.True(entityTypeBuilder.PrimitiveCollection(a => a.PrimitiveCollectionWithNullableElement).ElementType().Metadata.IsNullable); + } + private void RunConvention(InternalPropertyBuilder propertyBuilder) { var context = new ConventionContext( @@ -146,6 +165,9 @@ public string NonNullable [Required] public string? RequiredAndNullable { get; set; } + public List PrimitiveCollectionWithNonNullableElement { get; set; } = new(); + public List PrimitiveCollectionWithNullableElement { get; set; } = new(); + #nullable disable #pragma warning disable CS8632 // The annotation for nullable reference types should only be used in code within a '#nullable' context. public string NullObliviousNonNullable { get; set; } diff --git a/test/EFCore.Tests/ModelBuilding/NonRelationshipTestBase.cs b/test/EFCore.Tests/ModelBuilding/NonRelationshipTestBase.cs index 9413ae7ec64..606521fdfe9 100644 --- a/test/EFCore.Tests/ModelBuilding/NonRelationshipTestBase.cs +++ b/test/EFCore.Tests/ModelBuilding/NonRelationshipTestBase.cs @@ -3061,7 +3061,7 @@ public virtual void Element_types_are_nullable_by_default_if_the_type_is_nullabl var entityType = modelBuilder.FinalizeModel().FindEntityType(typeof(CollectionQuarks))!; Assert.False(entityType.FindProperty(nameof(CollectionQuarks.Up))!.GetElementType()!.IsNullable); - Assert.True(entityType.FindProperty(nameof(CollectionQuarks.Down))!.GetElementType()!.IsNullable); // Issue #31416 + Assert.False(entityType.FindProperty(nameof(CollectionQuarks.Down))!.GetElementType()!.IsNullable); Assert.True(entityType.FindProperty("Charm")!.GetElementType()!.IsNullable); Assert.True(entityType.FindProperty("Strange")!.GetElementType()!.IsNullable); Assert.True(entityType.FindProperty("Stranger")!.GetElementType()!.IsNullable);