diff --git a/src/EFCore.Design/Query/Internal/PrecompiledQueryCodeGenerator.cs b/src/EFCore.Design/Query/Internal/PrecompiledQueryCodeGenerator.cs index 9b60e2a1d3c..d11cf7f07f2 100644 --- a/src/EFCore.Design/Query/Internal/PrecompiledQueryCodeGenerator.cs +++ b/src/EFCore.Design/Query/Internal/PrecompiledQueryCodeGenerator.cs @@ -797,7 +797,7 @@ or nameof(EntityFrameworkQueryableExtensions.ExecuteUpdateAsync), { // Special case: this is a non-lambda argument (Skip/Take/FromSql). // Simply add the argument directly as a parameter - code.AppendLine($"""queryContext.AddParameter("{evaluatableRootPaths.ParameterName}", {parameterName});"""); + code.AppendLine($"""queryContext.Parameters.Add("{evaluatableRootPaths.ParameterName}", {parameterName});"""); continue; } @@ -849,7 +849,7 @@ void GenerateCapturedVariableExtractors( // (see ExpressionTreeFuncletizer.Evaluate()). // TODO: Basically this means that the evaluator should come from ExpressionTreeFuncletizer itself, as part of its outputs // TODO: Integrate try/catch around the evaluation? - code.AppendLine("queryContext.AddParameter("); + code.AppendLine("queryContext.Parameters.Add("); using (code.Indent()) { code @@ -893,7 +893,7 @@ void GenerateCapturedVariableExtractors( }; code.AppendLine( - $"""queryContext.AddParameter("{evaluatableRootPaths.ParameterName}", {argumentsParameter});"""); + $"""queryContext.Parameters.Add("{evaluatableRootPaths.ParameterName}", {argumentsParameter});"""); break; } diff --git a/src/EFCore.Relational/Extensions/Internal/RelationalCommandResolverExtensions.cs b/src/EFCore.Relational/Extensions/Internal/RelationalCommandResolverExtensions.cs index 5723ef60360..5dab1a56b80 100644 --- a/src/EFCore.Relational/Extensions/Internal/RelationalCommandResolverExtensions.cs +++ b/src/EFCore.Relational/Extensions/Internal/RelationalCommandResolverExtensions.cs @@ -24,7 +24,7 @@ public static IRelationalCommand RentAndPopulateRelationalCommand( this RelationalCommandResolver relationalCommandResolver, RelationalQueryContext queryContext) { - var relationalCommandTemplate = relationalCommandResolver(queryContext.ParameterValues); + var relationalCommandTemplate = relationalCommandResolver(queryContext.Parameters); var relationalCommand = queryContext.Connection.RentCommand(); relationalCommand.PopulateFrom(relationalCommandTemplate); return relationalCommand; diff --git a/src/EFCore.Relational/Infrastructure/RelationalDbContextOptionsBuilder.cs b/src/EFCore.Relational/Infrastructure/RelationalDbContextOptionsBuilder.cs index 725b51b9ba0..21e3de1ba48 100644 --- a/src/EFCore.Relational/Infrastructure/RelationalDbContextOptionsBuilder.cs +++ b/src/EFCore.Relational/Infrastructure/RelationalDbContextOptionsBuilder.cs @@ -163,10 +163,9 @@ public virtual TBuilder ExecutionStrategy( /// /// /// - /// When a LINQ query contains a parameterized collection, by default EF Core parameterizes the entire collection as a single - /// SQL parameter, if possible. For example, on SQL Server, the LINQ query Where(b => ids.Contains(b.Id) is translated to - /// WHERE [b].[Id] IN (SELECT [i].[value] FROM OPENJSON(@__ids_0) ...). While this helps with query plan caching, it can - /// produce worse query plans for certain query types. + /// When a LINQ query contains a parameterized collection, by default EF Core translates as a multiple SQL parameters, + /// if possible. For example, on SQL Server, the LINQ query Where(b => ids.Contains(b.Id) is translated to + /// WHERE [b].[Id] IN (@ids1, @ids2, @ids3). /// /// /// instructs EF to translate the collection to a set of constants: @@ -176,37 +175,57 @@ public virtual TBuilder ExecutionStrategy( /// /// Note that it's possible to cause EF to translate a specific collection in a specific query to constants by wrapping the /// parameterized collection in : Where(b => EF.Constant(ids).Contains(b.Id). This overrides - /// the default. Likewise, you can translate a specific collection in a specific query to a single parameter by wrapping the - /// parameterized collection in : Where(b => EF.Parameter(ids).Contains(b.Id). This - /// overrides the setting. + /// the default. /// /// public virtual TBuilder TranslateParameterizedCollectionsToConstants() => WithOption(e => (TExtension)e.WithParameterizedCollectionTranslationMode(ParameterizedCollectionTranslationMode.Constantize)); /// - /// Configures the context to translate parameterized collections to parameters. + /// Configures the context to translate parameterized collections to a single array-like parameter. /// /// /// - /// When a LINQ query contains a parameterized collection, by default EF Core parameterizes the entire collection as a single - /// SQL parameter, if possible. For example, on SQL Server, the LINQ query Where(b => ids.Contains(b.Id) is translated to - /// WHERE [b].[Id] IN (SELECT [i].[value] FROM OPENJSON(@__ids_0) ...). While this helps with query plan caching, it can - /// produce worse query plans for certain query types. + /// When a LINQ query contains a parameterized collection, by default EF Core translates as a multiple SQL parameters, + /// if possible. For example, on SQL Server, the LINQ query Where(b => ids.Contains(b.Id) is translated to + /// WHERE [b].[Id] IN (@ids1, @ids2, @ids3). /// /// - /// explicitly instructs EF to perform the default translation - /// of parameterized collections, which is translating them to parameters. + /// instructs EF to translate the collection to a single array-like parameter: + /// WHERE [b].[Id] IN (SELECT [i].[value] FROM OPENJSON(@ids) ...). /// /// - /// Note that it's possible to cause EF to translate a specific collection in a specific query to constants by wrapping the - /// parameterized collection in : Where(b => EF.Constant(ids).Contains(b.Id). This overrides + /// Note that it's possible to cause EF to translate a specific collection in a specific query to parameter by wrapping the + /// parameterized collection in : Where(b => EF.Parameter(ids).Contains(b.Id). This overrides /// the default. /// /// public virtual TBuilder TranslateParameterizedCollectionsToParameters() => WithOption(e => (TExtension)e.WithParameterizedCollectionTranslationMode(ParameterizedCollectionTranslationMode.Parameterize)); + /// + /// Configures the context to translate parameterized collections to expanded parameters. + /// + /// + /// + /// When a LINQ query contains a parameterized collection, by default EF Core translates as a multiple SQL parameters, + /// if possible. For example, on SQL Server, the LINQ query Where(b => ids.Contains(b.Id) is translated to + /// WHERE [b].[Id] IN (@ids1, @ids2, @ids3). + /// + /// + /// instructs EF to translate the collection to a set of parameters: + /// WHERE [b].[Id] IN (@ids1, @ids2, @ids3). + /// + /// + //TODO: When appropriate EF method is implemented, mention it here. + // + // Note that it's possible to cause EF to translate a specific collection in a specific query to expanded parameters by wrapping the + // parameterized collection in : Where(b => EF.Parameter(ids).???(b.Id). This overrides + // the default. + // + public virtual TBuilder TranslateParameterizedCollectionsToExpandedParameters() + => WithOption(e => (TExtension)e.WithParameterizedCollectionTranslationMode(ParameterizedCollectionTranslationMode.ParameterizeExpanded)); + /// /// Sets an option by cloning the extension used to store the settings. This ensures the builder /// does not modify options that are already in use elsewhere. diff --git a/src/EFCore.Relational/Internal/ParameterizedCollectionTranslationMode.cs b/src/EFCore.Relational/Internal/ParameterizedCollectionTranslationMode.cs index cfb59de6f08..124fffbfa67 100644 --- a/src/EFCore.Relational/Internal/ParameterizedCollectionTranslationMode.cs +++ b/src/EFCore.Relational/Internal/ParameterizedCollectionTranslationMode.cs @@ -26,4 +26,12 @@ public enum ParameterizedCollectionTranslationMode /// doing so can result in application failures when updating to a new Entity Framework Core release. /// Parameterize, + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + ParameterizeExpanded, } diff --git a/src/EFCore.Relational/Query/CacheSafeParameterFacade.cs b/src/EFCore.Relational/Query/CacheSafeParameterFacade.cs new file mode 100644 index 00000000000..35023bf69cd --- /dev/null +++ b/src/EFCore.Relational/Query/CacheSafeParameterFacade.cs @@ -0,0 +1,45 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace Microsoft.EntityFrameworkCore.Query; + +/// +/// A facade over which provides cache-safe way to access parameters after the SQL cache. +/// +/// +/// The SQL cache only includes then nullability of parameters in its cache key. Accordingly, this type exposes an API for checking +/// the nullability of a parameter. It also allows retrieving the full parameter dictionary for arbitrary checks, but when this +/// API is called, the facade records this fact, and the resulting SQL will not get cached. +/// +public sealed class CacheSafeParameterFacade(Dictionary parameters) +{ + /// + /// Returns whether the parameter with the given name is null. + /// + /// + /// The method assumes that the parameter with the given name exists in the dictionary, + /// and otherwise throws . + /// + public bool IsParameterNull(string parameterName) + => !parameters.TryGetValue(parameterName, out var value) + ? throw new UnreachableException($"Parameter with name '{parameterName}' does not exist.") + : value is null; + + /// + /// Returns the full dictionary of parameters, and disables caching for the generated SQL. + /// + public Dictionary GetParametersAndDisableSqlCaching() + { + CanCache = false; + + return parameters; + } + + /// + /// Whether the SQL generated using this facade can be cached, i.e. whether the full dictionary of parameters + /// has been accessed. + /// + public bool CanCache { get; private set; } = true; +} diff --git a/src/EFCore.Relational/Query/Internal/FromSqlQueryingEnumerable.cs b/src/EFCore.Relational/Query/Internal/FromSqlQueryingEnumerable.cs index 3bdd96e16b3..f57f117b049 100644 --- a/src/EFCore.Relational/Query/Internal/FromSqlQueryingEnumerable.cs +++ b/src/EFCore.Relational/Query/Internal/FromSqlQueryingEnumerable.cs @@ -128,11 +128,11 @@ IEnumerator IEnumerable.GetEnumerator() /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public virtual DbCommand CreateDbCommand() - => _relationalCommandResolver(_relationalQueryContext.ParameterValues) + => _relationalCommandResolver(_relationalQueryContext.Parameters) .CreateDbCommand( new RelationalCommandParameterObject( _relationalQueryContext.Connection, - _relationalQueryContext.ParameterValues, + _relationalQueryContext.Parameters, null, null, null, @@ -269,7 +269,7 @@ private static bool InitializeReader(Enumerator enumerator) enumerator._dataReader = relationalCommand.ExecuteReader( new RelationalCommandParameterObject( enumerator._relationalQueryContext.Connection, - enumerator._relationalQueryContext.ParameterValues, + enumerator._relationalQueryContext.Parameters, enumerator._readerColumns, enumerator._relationalQueryContext.Context, enumerator._relationalQueryContext.CommandLogger, @@ -384,7 +384,7 @@ private static async Task InitializeReaderAsync(AsyncEnumerator enumerator enumerator._dataReader = await relationalCommand.ExecuteReaderAsync( new RelationalCommandParameterObject( enumerator._relationalQueryContext.Connection, - enumerator._relationalQueryContext.ParameterValues, + enumerator._relationalQueryContext.Parameters, enumerator._readerColumns, enumerator._relationalQueryContext.Context, enumerator._relationalQueryContext.CommandLogger, diff --git a/src/EFCore.Relational/Query/Internal/GroupBySingleQueryingEnumerable.cs b/src/EFCore.Relational/Query/Internal/GroupBySingleQueryingEnumerable.cs index 7caf9498228..af0eb423bff 100644 --- a/src/EFCore.Relational/Query/Internal/GroupBySingleQueryingEnumerable.cs +++ b/src/EFCore.Relational/Query/Internal/GroupBySingleQueryingEnumerable.cs @@ -139,11 +139,11 @@ IEnumerator IEnumerable.GetEnumerator() /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public virtual DbCommand CreateDbCommand() - => _relationalCommandResolver(_relationalQueryContext.ParameterValues) + => _relationalCommandResolver(_relationalQueryContext.Parameters) .CreateDbCommand( new RelationalCommandParameterObject( _relationalQueryContext.Connection, - _relationalQueryContext.ParameterValues, + _relationalQueryContext.Parameters, null, null, null, CommandSource.LinqQuery), @@ -339,7 +339,7 @@ private static bool InitializeReader(Enumerator enumerator) var dataReader = enumerator._dataReader = relationalCommand.ExecuteReader( new RelationalCommandParameterObject( enumerator._relationalQueryContext.Connection, - enumerator._relationalQueryContext.ParameterValues, + enumerator._relationalQueryContext.Parameters, enumerator._readerColumns, enumerator._relationalQueryContext.Context, enumerator._relationalQueryContext.CommandLogger, @@ -519,7 +519,7 @@ private static async Task InitializeReaderAsync(AsyncEnumerator enumerator var dataReader = enumerator._dataReader = await relationalCommand.ExecuteReaderAsync( new RelationalCommandParameterObject( enumerator._relationalQueryContext.Connection, - enumerator._relationalQueryContext.ParameterValues, + enumerator._relationalQueryContext.Parameters, enumerator._readerColumns, enumerator._relationalQueryContext.Context, enumerator._relationalQueryContext.CommandLogger, diff --git a/src/EFCore.Relational/Query/Internal/GroupBySplitQueryingEnumerable.cs b/src/EFCore.Relational/Query/Internal/GroupBySplitQueryingEnumerable.cs index 982b59dcdb6..e8b67872908 100644 --- a/src/EFCore.Relational/Query/Internal/GroupBySplitQueryingEnumerable.cs +++ b/src/EFCore.Relational/Query/Internal/GroupBySplitQueryingEnumerable.cs @@ -149,11 +149,11 @@ IEnumerator IEnumerable.GetEnumerator() /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public virtual DbCommand CreateDbCommand() - => _relationalCommandResolver(_relationalQueryContext.ParameterValues) + => _relationalCommandResolver(_relationalQueryContext.Parameters) .CreateDbCommand( new RelationalCommandParameterObject( _relationalQueryContext.Connection, - _relationalQueryContext.ParameterValues, + _relationalQueryContext.Parameters, null, null, null, CommandSource.LinqQuery), @@ -339,7 +339,7 @@ private static bool InitializeReader(Enumerator enumerator) var dataReader = enumerator._dataReader = relationalCommand.ExecuteReader( new RelationalCommandParameterObject( enumerator._relationalQueryContext.Connection, - enumerator._relationalQueryContext.ParameterValues, + enumerator._relationalQueryContext.Parameters, enumerator._readerColumns, enumerator._relationalQueryContext.Context, enumerator._relationalQueryContext.CommandLogger, @@ -510,7 +510,7 @@ private static async Task InitializeReaderAsync(AsyncEnumerator enumerator var dataReader = enumerator._dataReader = await relationalCommand.ExecuteReaderAsync( new RelationalCommandParameterObject( enumerator._relationalQueryContext.Connection, - enumerator._relationalQueryContext.ParameterValues, + enumerator._relationalQueryContext.Parameters, enumerator._readerColumns, enumerator._relationalQueryContext.Context, enumerator._relationalQueryContext.CommandLogger, diff --git a/src/EFCore.Relational/Query/Internal/RelationalCommandCache.cs b/src/EFCore.Relational/Query/Internal/RelationalCommandCache.cs index a29886eb9d3..e1279bf577d 100644 --- a/src/EFCore.Relational/Query/Internal/RelationalCommandCache.cs +++ b/src/EFCore.Relational/Query/Internal/RelationalCommandCache.cs @@ -3,6 +3,7 @@ using System.Collections.Concurrent; using System.Runtime.CompilerServices; +using Microsoft.EntityFrameworkCore.Internal; using Microsoft.Extensions.Caching.Memory; namespace Microsoft.EntityFrameworkCore.Query.Internal; @@ -34,13 +35,14 @@ public RelationalCommandCache( IQuerySqlGeneratorFactory querySqlGeneratorFactory, IRelationalParameterBasedSqlProcessorFactory relationalParameterBasedSqlProcessorFactory, Expression queryExpression, - bool useRelationalNulls) + bool useRelationalNulls, + ParameterizedCollectionTranslationMode? parameterizedCollectionTranslationMode) { _memoryCache = memoryCache; _querySqlGeneratorFactory = querySqlGeneratorFactory; _queryExpression = queryExpression; _relationalParameterBasedSqlProcessor = relationalParameterBasedSqlProcessorFactory.Create( - new RelationalParameterBasedSqlProcessorParameters(useRelationalNulls)); + new RelationalParameterBasedSqlProcessorParameters(useRelationalNulls, parameterizedCollectionTranslationMode)); } /// @@ -49,7 +51,7 @@ public RelationalCommandCache( /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// - public virtual IRelationalCommandTemplate GetRelationalCommandTemplate(IReadOnlyDictionary parameters) + public virtual IRelationalCommandTemplate GetRelationalCommandTemplate(Dictionary parameters) { var cacheKey = new CommandCacheKey(_queryExpression, parameters); @@ -69,7 +71,7 @@ public virtual IRelationalCommandTemplate GetRelationalCommandTemplate(IReadOnly { if (!_memoryCache.TryGetValue(cacheKey, out relationalCommandTemplate)) { - var queryExpression = _relationalParameterBasedSqlProcessor.Optimize( + var queryExpression = _relationalParameterBasedSqlProcessor.Process( _queryExpression, parameters, out var canCache); relationalCommandTemplate = _querySqlGeneratorFactory.Create().GetCommand(queryExpression); diff --git a/src/EFCore.Relational/Query/Internal/RelationalCommandResolver.cs b/src/EFCore.Relational/Query/Internal/RelationalCommandResolver.cs index 2e73b0413b9..980d430e44a 100644 --- a/src/EFCore.Relational/Query/Internal/RelationalCommandResolver.cs +++ b/src/EFCore.Relational/Query/Internal/RelationalCommandResolver.cs @@ -9,4 +9,4 @@ namespace Microsoft.EntityFrameworkCore.Query.Internal; /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// -public delegate IRelationalCommandTemplate RelationalCommandResolver(IReadOnlyDictionary parameters); +public delegate IRelationalCommandTemplate RelationalCommandResolver(Dictionary parameters); diff --git a/src/EFCore.Relational/Query/Internal/RelationalParameterProcessor.cs b/src/EFCore.Relational/Query/Internal/RelationalParameterProcessor.cs index 4c4f2c85122..b955ccae286 100644 --- a/src/EFCore.Relational/Query/Internal/RelationalParameterProcessor.cs +++ b/src/EFCore.Relational/Query/Internal/RelationalParameterProcessor.cs @@ -35,9 +35,8 @@ private readonly IDictionary _visitedFromSqlExpre private readonly Dictionary _sqlParameters = new(); - private IReadOnlyDictionary _parametersValues; + private CacheSafeParameterFacade _parametersFacade; private ParameterNameGenerator _parameterNameGenerator; - private bool _canCache; /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -54,7 +53,7 @@ public RelationalParameterProcessor( _typeMappingSource = dependencies.TypeMappingSource; _parameterNameGeneratorFactory = dependencies.ParameterNameGeneratorFactory; _sqlGenerationHelper = dependencies.SqlGenerationHelper; - _parametersValues = default!; + _parametersFacade = default!; _parameterNameGenerator = default!; } @@ -69,20 +68,15 @@ public RelationalParameterProcessor( /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// - public virtual Expression Expand( - Expression queryExpression, - IReadOnlyDictionary parameterValues, - out bool canCache) + public virtual Expression Expand(Expression queryExpression, CacheSafeParameterFacade parametersFacade) { _visitedFromSqlExpressions.Clear(); _prefixedParameterNames.Clear(); _sqlParameters.Clear(); _parameterNameGenerator = _parameterNameGeneratorFactory.Create(); - _parametersValues = parameterValues; - _canCache = true; + _parametersFacade = parametersFacade; var result = Visit(queryExpression); - canCache = _canCache; return result; } @@ -146,8 +140,8 @@ private FromSqlExpression VisitFromSql(FromSqlExpression fromSql) { case QueryParameterExpression queryParameter: // parameter value will never be null. It could be empty object?[] - var parameterValues = (object?[])_parametersValues[queryParameter.Name]!; - _canCache = false; + var parameters = _parametersFacade.GetParametersAndDisableSqlCaching(); + var parameterValues = (object?[])parameters[queryParameter.Name]!; var subParameters = new List(parameterValues.Length); // ReSharper disable once ForCanBeConvertedToForeach diff --git a/src/EFCore.Relational/Query/Internal/RelationalProjectionBindingExpressionVisitor.cs b/src/EFCore.Relational/Query/Internal/RelationalProjectionBindingExpressionVisitor.cs index dc2831a8d5d..937410dd762 100644 --- a/src/EFCore.Relational/Query/Internal/RelationalProjectionBindingExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/Internal/RelationalProjectionBindingExpressionVisitor.cs @@ -661,7 +661,7 @@ private ProjectionBindingExpression AddClientProjection(Expression expression, T /// public static T GetParameterValue(QueryContext queryContext, string parameterName) #pragma warning restore IDE0052 // Remove unread private members - => (T)queryContext.ParameterValues[parameterName]!; + => (T)queryContext.Parameters[parameterName]!; private sealed class IncludeFindingExpressionVisitor : ExpressionVisitor { diff --git a/src/EFCore.Relational/Query/Internal/SingleQueryingEnumerable.cs b/src/EFCore.Relational/Query/Internal/SingleQueryingEnumerable.cs index d3037ae67f8..5a79eb34b9e 100644 --- a/src/EFCore.Relational/Query/Internal/SingleQueryingEnumerable.cs +++ b/src/EFCore.Relational/Query/Internal/SingleQueryingEnumerable.cs @@ -123,11 +123,11 @@ IEnumerator IEnumerable.GetEnumerator() /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public virtual DbCommand CreateDbCommand() - => _relationalCommandResolver(_relationalQueryContext.ParameterValues) + => _relationalCommandResolver(_relationalQueryContext.Parameters) .CreateDbCommand( new RelationalCommandParameterObject( _relationalQueryContext.Connection, - _relationalQueryContext.ParameterValues, + _relationalQueryContext.Parameters, null, null, null, CommandSource.LinqQuery), @@ -266,7 +266,7 @@ private static bool InitializeReader(Enumerator enumerator) var dataReader = enumerator._dataReader = relationalCommand.ExecuteReader( new RelationalCommandParameterObject( enumerator._relationalQueryContext.Connection, - enumerator._relationalQueryContext.ParameterValues, + enumerator._relationalQueryContext.Parameters, enumerator._readerColumns, enumerator._relationalQueryContext.Context, enumerator._relationalQueryContext.CommandLogger, @@ -420,7 +420,7 @@ private static async Task InitializeReaderAsync(AsyncEnumerator enumerator var dataReader = enumerator._dataReader = await relationalCommand.ExecuteReaderAsync( new RelationalCommandParameterObject( enumerator._relationalQueryContext.Connection, - enumerator._relationalQueryContext.ParameterValues, + enumerator._relationalQueryContext.Parameters, enumerator._readerColumns, enumerator._relationalQueryContext.Context, enumerator._relationalQueryContext.CommandLogger, diff --git a/src/EFCore.Relational/Query/Internal/SplitQueryingEnumerable.cs b/src/EFCore.Relational/Query/Internal/SplitQueryingEnumerable.cs index 5a8e99b477c..2a7140594ac 100644 --- a/src/EFCore.Relational/Query/Internal/SplitQueryingEnumerable.cs +++ b/src/EFCore.Relational/Query/Internal/SplitQueryingEnumerable.cs @@ -133,11 +133,11 @@ IEnumerator IEnumerable.GetEnumerator() /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public virtual DbCommand CreateDbCommand() - => _relationalCommandResolver(_relationalQueryContext.ParameterValues) + => _relationalCommandResolver(_relationalQueryContext.Parameters) .CreateDbCommand( new RelationalCommandParameterObject( _relationalQueryContext.Connection, - _relationalQueryContext.ParameterValues, + _relationalQueryContext.Parameters, null, null, null, @@ -260,7 +260,7 @@ private static bool InitializeReader(Enumerator enumerator) var dataReader = enumerator._dataReader = relationalCommand.ExecuteReader( new RelationalCommandParameterObject( enumerator._relationalQueryContext.Connection, - enumerator._relationalQueryContext.ParameterValues, + enumerator._relationalQueryContext.Parameters, enumerator._readerColumns, enumerator._relationalQueryContext.Context, enumerator._relationalQueryContext.CommandLogger, @@ -408,7 +408,7 @@ private static async Task InitializeReaderAsync(AsyncEnumerator enumerator var dataReader = enumerator._dataReader = await relationalCommand.ExecuteReaderAsync( new RelationalCommandParameterObject( enumerator._relationalQueryContext.Connection, - enumerator._relationalQueryContext.ParameterValues, + enumerator._relationalQueryContext.Parameters, enumerator._readerColumns, enumerator._relationalQueryContext.Context, enumerator._relationalQueryContext.CommandLogger, diff --git a/src/EFCore.Relational/Query/RelationalParameterBasedSqlProcessor.cs b/src/EFCore.Relational/Query/RelationalParameterBasedSqlProcessor.cs index 876687e18b6..990e718f952 100644 --- a/src/EFCore.Relational/Query/RelationalParameterBasedSqlProcessor.cs +++ b/src/EFCore.Relational/Query/RelationalParameterBasedSqlProcessor.cs @@ -41,23 +41,30 @@ public RelationalParameterBasedSqlProcessor( protected virtual RelationalParameterBasedSqlProcessorParameters Parameters { get; } /// - /// Optimizes the query expression for given parameter values. + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. /// - /// A query expression to optimize. - /// A dictionary of parameter values to use. - /// A bool value indicating if the query expression can be cached. - /// An optimized query expression. - public virtual Expression Optimize( - Expression queryExpression, - IReadOnlyDictionary parametersValues, - out bool canCache) + [EntityFrameworkInternal] + public virtual Expression Process(Expression queryExpression, Dictionary parameters, out bool canCache) { - canCache = true; - queryExpression = ProcessSqlNullability(queryExpression, parametersValues, out var sqlNullabilityCanCache); - canCache &= sqlNullabilityCanCache; + var parametersFacade = new CacheSafeParameterFacade(parameters); + var result = Process(queryExpression, parametersFacade); + canCache = parametersFacade.CanCache; + + return result; + } - queryExpression = ExpandFromSqlParameter(queryExpression, parametersValues, out var fromSqlParameterCanCache); - canCache &= fromSqlParameterCanCache; + /// + /// Performs final query processing that takes parameter values into account. + /// + /// A query expression to process. + /// A facade allowing access to parameters in a cache-safe way. + public virtual Expression Process(Expression queryExpression, CacheSafeParameterFacade parametersFacade) + { + queryExpression = ProcessSqlNullability(queryExpression, parametersFacade); + queryExpression = ExpandFromSqlParameter(queryExpression, parametersFacade); return queryExpression; } @@ -67,25 +74,31 @@ public virtual Expression Optimize( /// optimize it for given parameter values. /// /// A query expression to optimize. - /// A dictionary of parameter values to use. - /// A bool value indicating if the query expression can be cached. + /// A facade allowing access to parameters in a cache-safe way. /// A processed query expression. - protected virtual Expression ProcessSqlNullability( - Expression queryExpression, - IReadOnlyDictionary parametersValues, - out bool canCache) - => new SqlNullabilityProcessor(Dependencies, Parameters).Process(queryExpression, parametersValues, out canCache); + protected virtual Expression ProcessSqlNullability(Expression queryExpression, CacheSafeParameterFacade parametersFacade) + => new SqlNullabilityProcessor(Dependencies, Parameters).Process(queryExpression, parametersFacade); /// /// Expands the parameters to inside the query expression for given parameter values. /// /// A query expression to optimize. + /// A facade allowing access to parameters in a cache-safe way. + /// A processed query expression. + protected virtual Expression ExpandFromSqlParameter(Expression queryExpression, CacheSafeParameterFacade parametersFacade) + => new RelationalParameterProcessor(Dependencies).Expand(queryExpression, parametersFacade); + + /// + /// Optimizes the query expression for given parameter values. + /// + /// A query expression to optimize. /// A dictionary of parameter values to use. /// A bool value indicating if the query expression can be cached. - /// A processed query expression. - protected virtual Expression ExpandFromSqlParameter( + /// An optimized query expression. + [Obsolete("Override Process() instead", error: true)] + public virtual Expression Optimize( Expression queryExpression, IReadOnlyDictionary parametersValues, out bool canCache) - => new RelationalParameterProcessor(Dependencies).Expand(queryExpression, parametersValues, out canCache); + => throw new UnreachableException(); } diff --git a/src/EFCore.Relational/Query/RelationalParameterBasedSqlProcessorParameters.cs b/src/EFCore.Relational/Query/RelationalParameterBasedSqlProcessorParameters.cs index e4e87ee1d01..fb91eda7168 100644 --- a/src/EFCore.Relational/Query/RelationalParameterBasedSqlProcessorParameters.cs +++ b/src/EFCore.Relational/Query/RelationalParameterBasedSqlProcessorParameters.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 Microsoft.EntityFrameworkCore.Internal; + namespace Microsoft.EntityFrameworkCore.Query; /// @@ -13,11 +15,20 @@ public sealed record RelationalParameterBasedSqlProcessorParameters /// public bool UseRelationalNulls { get; init; } + /// + /// A value indicating what parametrized collection translation mode should be used. + /// + public ParameterizedCollectionTranslationMode? ParameterizedCollectionTranslationMode { get; init; } + /// /// Creates a new instance of . /// /// A value indicating if relational nulls should be used. + /// A value indicating what translation mode should be used. [EntityFrameworkInternal] - public RelationalParameterBasedSqlProcessorParameters(bool useRelationalNulls) - => UseRelationalNulls = useRelationalNulls; + public RelationalParameterBasedSqlProcessorParameters(bool useRelationalNulls, ParameterizedCollectionTranslationMode? parameterizedCollectionTranslationMode) + { + UseRelationalNulls = useRelationalNulls; + ParameterizedCollectionTranslationMode = parameterizedCollectionTranslationMode; + } } diff --git a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.ExecuteUpdate.cs b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.ExecuteUpdate.cs index 51e2693b62d..308cf9489b1 100644 --- a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.ExecuteUpdate.cs +++ b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.ExecuteUpdate.cs @@ -560,7 +560,7 @@ protected virtual bool IsValidSelectExpressionForExecuteUpdate( List? complexPropertyChain, IProperty property) { - var baseValue = context.ParameterValues[baseParameterName]; + var baseValue = context.Parameters[baseParameterName]; if (complexPropertyChain is not null) { diff --git a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs index 3c7ca4a7f14..217bd870fb2 100644 --- a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs @@ -21,6 +21,7 @@ public partial class RelationalQueryableMethodTranslatingExpressionVisitor : Que private readonly IRelationalTypeMappingSource _typeMappingSource; private readonly ISqlExpressionFactory _sqlExpressionFactory; private readonly bool _subquery; + private readonly ParameterizedCollectionTranslationMode? _primitiveCollectionsBehavior; /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -63,6 +64,7 @@ public RelationalQueryableMethodTranslatingExpressionVisitor( _typeMappingSource = relationalDependencies.TypeMappingSource; _sqlExpressionFactory = sqlExpressionFactory; _subquery = false; + _primitiveCollectionsBehavior = RelationalOptionsExtension.Extract(_queryCompilationContext.ContextOptions).ParameterizedCollectionTranslationMode; } /// @@ -88,6 +90,7 @@ protected RelationalQueryableMethodTranslatingExpressionVisitor( _typeMappingSource = parentVisitor._typeMappingSource; _sqlExpressionFactory = parentVisitor._sqlExpressionFactory; _subquery = true; + _primitiveCollectionsBehavior = RelationalOptionsExtension.Extract(_queryCompilationContext.ContextOptions).ParameterizedCollectionTranslationMode; } /// @@ -293,13 +296,14 @@ JsonScalarExpression jsonScalar Check.DebugAssert(sqlParameterExpression is not null, "sqlParameterExpression is not null"); - var primitiveCollectionsBehavior = RelationalOptionsExtension.Extract(QueryCompilationContext.ContextOptions) - .ParameterizedCollectionTranslationMode; - var tableAlias = _sqlAliasManager.GenerateTableAlias(sqlParameterExpression.Name.TrimStart('_')); - if (queryParameter.ShouldBeConstantized - || (primitiveCollectionsBehavior == ParameterizedCollectionTranslationMode.Constantize - && !queryParameter.ShouldNotBeConstantized)) + + var constantize = queryParameter.ShouldBeConstantized + || (_primitiveCollectionsBehavior is ParameterizedCollectionTranslationMode.Constantize + && !queryParameter.ShouldNotBeConstantized); + var parameterizeExpanded = (_primitiveCollectionsBehavior is null or ParameterizedCollectionTranslationMode.ParameterizeExpanded) + && !queryParameter.ShouldNotBeConstantized; + if (constantize || parameterizeExpanded) { var valuesExpression = new ValuesExpression( tableAlias, @@ -569,16 +573,21 @@ protected override ShapedQueryExpression TranslateConcat(ShapedQueryExpression s return TranslateAny(source, anyLambda); } - // Pattern-match Contains over ValuesExpression, translating to simplified 'item IN (1, 2, 3)' with constant elements + // Pattern-match Contains over ValuesExpression, translating to simplified 'item IN (1, 2, 3)' with constant elements. if (TryExtractBareInlineCollectionValues(source, out var values, out var valuesParameter)) { - var inExpression = (values, valuesParameter) switch + if (values is not null) { - (not null, null) => _sqlExpressionFactory.In(translatedItem, values), - (null, not null) => _sqlExpressionFactory.In(translatedItem, valuesParameter), - _ => throw new UnreachableException(), - }; - return source.Update(new SelectExpression(inExpression, _sqlAliasManager), source.ShaperExpression); + var inExpression = _sqlExpressionFactory.In(translatedItem, values); + return source.Update(new SelectExpression(inExpression, _sqlAliasManager), source.ShaperExpression); + } + if (valuesParameter is not null + // Expanding parameters will happen in 2nd stage of query pipeline. + && _primitiveCollectionsBehavior is not (null or ParameterizedCollectionTranslationMode.ParameterizeExpanded)) + { + var inExpression = _sqlExpressionFactory.In(translatedItem, valuesParameter); + return source.Update(new SelectExpression(inExpression, _sqlAliasManager), source.ShaperExpression); + } } // Translate to IN with a subquery. diff --git a/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.ClientMethods.cs b/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.ClientMethods.cs index 664151585da..419e8e7bbea 100644 --- a/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.ClientMethods.cs +++ b/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.ClientMethods.cs @@ -447,7 +447,7 @@ static RelationalDataReader InitializeReader( return relationalCommand.ExecuteReader( new RelationalCommandParameterObject( queryContext.Connection, - queryContext.ParameterValues, + queryContext.Parameters, readerColumns, queryContext.Context, queryContext.CommandLogger, @@ -543,7 +543,7 @@ static async Task InitializeReaderAsync( return await relationalCommand.ExecuteReaderAsync( new RelationalCommandParameterObject( queryContext.Connection, - queryContext.ParameterValues, + queryContext.Parameters, readerColumns, queryContext.Context, queryContext.CommandLogger, @@ -812,7 +812,7 @@ static RelationalDataReader InitializeReader( return relationalCommand.ExecuteReader( new RelationalCommandParameterObject( queryContext.Connection, - queryContext.ParameterValues, + queryContext.Parameters, readerColumns, queryContext.Context, queryContext.CommandLogger, @@ -903,7 +903,7 @@ static async Task InitializeReaderAsync( return await relationalCommand.ExecuteReaderAsync( new RelationalCommandParameterObject( queryContext.Connection, - queryContext.ParameterValues, + queryContext.Parameters, readerColumns, queryContext.Context, queryContext.CommandLogger, diff --git a/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.cs b/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.cs index fa5f9d7323a..9375cec8e64 100644 --- a/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.cs @@ -19,6 +19,7 @@ public partial class RelationalShapedQueryCompilingExpressionVisitor : ShapedQue private readonly bool _threadSafetyChecksEnabled; private readonly bool _detailedErrorsEnabled; private readonly bool _useRelationalNulls; + private readonly ParameterizedCollectionTranslationMode? _parameterizedCollectionTranslationMode; private readonly bool _isPrecompiling; private readonly RelationalParameterBasedSqlProcessor _relationalParameterBasedSqlProcessor; @@ -54,7 +55,7 @@ public RelationalShapedQueryCompilingExpressionVisitor( _relationalParameterBasedSqlProcessor = relationalDependencies.RelationalParameterBasedSqlProcessorFactory.Create( - new RelationalParameterBasedSqlProcessorParameters(_useRelationalNulls)); + new RelationalParameterBasedSqlProcessorParameters(_useRelationalNulls, _parameterizedCollectionTranslationMode)); _querySqlGeneratorFactory = relationalDependencies.QuerySqlGeneratorFactory; _contextType = queryCompilationContext.ContextType; @@ -62,6 +63,7 @@ public RelationalShapedQueryCompilingExpressionVisitor( _threadSafetyChecksEnabled = dependencies.CoreSingletonOptions.AreThreadSafetyChecksEnabled; _detailedErrorsEnabled = dependencies.CoreSingletonOptions.AreDetailedErrorsEnabled; _useRelationalNulls = RelationalOptionsExtension.Extract(queryCompilationContext.ContextOptions).UseRelationalNulls; + _parameterizedCollectionTranslationMode = RelationalOptionsExtension.Extract(queryCompilationContext.ContextOptions).ParameterizedCollectionTranslationMode; _isPrecompiling = queryCompilationContext.IsPrecompiling; } @@ -145,7 +147,7 @@ public static int NonQueryResult( return relationalCommand.ExecuteNonQuery( new RelationalCommandParameterObject( state.relationalQueryContext.Connection, - state.relationalQueryContext.ParameterValues, + state.relationalQueryContext.Parameters, null, state.relationalQueryContext.Context, state.relationalQueryContext.CommandLogger, @@ -212,7 +214,7 @@ public static Task NonQueryResultAsync( return relationalCommand.ExecuteNonQueryAsync( new RelationalCommandParameterObject( state.relationalQueryContext.Connection, - state.relationalQueryContext.ParameterValues, + state.relationalQueryContext.Parameters, null, state.relationalQueryContext.Context, state.relationalQueryContext.CommandLogger, @@ -497,7 +499,8 @@ private Expression CreateRelationalCommandResolverExpression(Expression queryExp RelationalDependencies.QuerySqlGeneratorFactory, RelationalDependencies.RelationalParameterBasedSqlProcessorFactory, queryExpression, - _useRelationalNulls); + _useRelationalNulls, + _parameterizedCollectionTranslationMode); var commandLiftableConstant = RelationalDependencies.RelationalLiftableConstantFactory.CreateLiftableConstant( relationalCommandCache, @@ -505,7 +508,7 @@ private Expression CreateRelationalCommandResolverExpression(Expression queryExp "relationalCommandCache", typeof(RelationalCommandCache)); - var parametersParameter = Parameter(typeof(IReadOnlyDictionary), "parameters"); + var parametersParameter = Parameter(typeof(Dictionary), "parameters"); return Lambda( Call( @@ -542,7 +545,7 @@ bool TryGeneratePregeneratedCommandResolver( return false; } - var parameterDictionaryParameter = Parameter(typeof(IReadOnlyDictionary), "parameters"); + var parameterDictionaryParameter = Parameter(typeof(Dictionary), "parameters"); var resultParameter = Parameter(typeof(IRelationalCommandTemplate), "result"); Expression resolverBody; bool canCache; @@ -657,9 +660,9 @@ static object GenerateNonNullParameterValue(Type type) } } - Expression GenerateRelationalCommandExpression(IReadOnlyDictionary parameters, out bool canCache) + Expression GenerateRelationalCommandExpression(Dictionary parameters, out bool canCache) { - var queryExpression = _relationalParameterBasedSqlProcessor.Optimize(select, parameters, out canCache); + var queryExpression = _relationalParameterBasedSqlProcessor.Process(select, parameters, out canCache); if (!canCache) { return null!; @@ -747,7 +750,8 @@ Expression> Generate MakeMemberAccess(contextParameter, _relationalDependenciesProperty), _relationalDependenciesRelationalParameterBasedSqlProcessorFactoryProperty), Constant(queryExpression), - Constant(_useRelationalNulls)), + Constant(_useRelationalNulls), + Constant(_parameterizedCollectionTranslationMode, typeof(ParameterizedCollectionTranslationMode?))), contextParameter); } } diff --git a/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs b/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs index 3a2fdd6b725..c8f4ec36332 100644 --- a/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs @@ -2097,7 +2097,7 @@ when memberInitExpression.Bindings.SingleOrDefault(mb => mb.Member.Name == compl List? complexPropertyChain, IProperty property) { - var baseValue = context.ParameterValues[baseParameterName]; + var baseValue = context.Parameters[baseParameterName]; if (complexPropertyChain is not null) { @@ -2127,7 +2127,7 @@ when memberInitExpression.Bindings.SingleOrDefault(mb => mb.Member.Name == compl string baseParameterName, IProperty property) { - if (context.ParameterValues[baseParameterName] is not IEnumerable baseListParameter) + if (context.Parameters[baseParameterName] is not IEnumerable baseListParameter) { return null; } diff --git a/src/EFCore.Relational/Query/SqlExpressions/SqlParameterExpression.cs b/src/EFCore.Relational/Query/SqlExpressions/SqlParameterExpression.cs index cb6690997fb..48c1602fbaa 100644 --- a/src/EFCore.Relational/Query/SqlExpressions/SqlParameterExpression.cs +++ b/src/EFCore.Relational/Query/SqlExpressions/SqlParameterExpression.cs @@ -24,7 +24,7 @@ public SqlParameterExpression(string name, Type type, RelationalTypeMapping? typ /// /// Creates a new instance of the class. /// - /// The name of the parameter as it is recorded in . + /// The name of the parameter as it is recorded in . /// /// The name of the parameter as it will be set on and inside the SQL as a placeholder /// (before any additional placeholder character prefixing). @@ -49,7 +49,7 @@ public SqlParameterExpression( } /// - /// The name of the parameter as it is recorded in . + /// The name of the parameter as it is recorded in . /// public string InvariantName { get; } diff --git a/src/EFCore.Relational/Query/SqlExpressions/ValuesExpression.cs b/src/EFCore.Relational/Query/SqlExpressions/ValuesExpression.cs index 5c79b84f3bb..b720bbb025a 100644 --- a/src/EFCore.Relational/Query/SqlExpressions/ValuesExpression.cs +++ b/src/EFCore.Relational/Query/SqlExpressions/ValuesExpression.cs @@ -167,7 +167,7 @@ public override Expression Quote() Constant(Alias, typeof(string)), RowValues is not null ? NewArrayInit(typeof(RowValueExpression), RowValues.Select(rv => rv.Quote())) - : Constant(null, typeof(RowValueExpression)), + : Constant(null, typeof(IReadOnlyList)), RelationalExpressionQuotingUtilities.QuoteOrNull(ValuesParameter), NewArrayInit(typeof(string), ColumnNames.Select(Constant)), RelationalExpressionQuotingUtilities.QuoteAnnotations(Annotations)); diff --git a/src/EFCore.Relational/Query/SqlNullabilityProcessor.cs b/src/EFCore.Relational/Query/SqlNullabilityProcessor.cs index a26ca0af260..403598cb8f1 100644 --- a/src/EFCore.Relational/Query/SqlNullabilityProcessor.cs +++ b/src/EFCore.Relational/Query/SqlNullabilityProcessor.cs @@ -3,7 +3,9 @@ using System.Collections; using System.Diagnostics.CodeAnalysis; +using Microsoft.EntityFrameworkCore.Internal; using Microsoft.EntityFrameworkCore.Query.SqlExpressions; +using PCTM = Microsoft.EntityFrameworkCore.Internal.ParameterizedCollectionTranslationMode; namespace Microsoft.EntityFrameworkCore.Query; @@ -22,7 +24,7 @@ public class SqlNullabilityProcessor : ExpressionVisitor private readonly List _nonNullableColumns; private readonly List _nullValueColumns; private readonly ISqlExpressionFactory _sqlExpressionFactory; - private bool _canCache; + private readonly Dictionary> _parametersForValues; /// /// Creates a new instance of the class. @@ -35,11 +37,13 @@ public SqlNullabilityProcessor( { Dependencies = dependencies; UseRelationalNulls = parameters.UseRelationalNulls; + ParameterizedCollectionTranslationMode = parameters.ParameterizedCollectionTranslationMode; _sqlExpressionFactory = dependencies.SqlExpressionFactory; _nonNullableColumns = []; _nullValueColumns = []; - ParameterValues = null!; + _parametersForValues = []; + ParametersFacade = null!; } /// @@ -52,41 +56,34 @@ public SqlNullabilityProcessor( /// protected virtual bool UseRelationalNulls { get; } + /// + /// A value indicating what translation mode to use. + /// + public virtual ParameterizedCollectionTranslationMode? ParameterizedCollectionTranslationMode { get; } + /// /// Dictionary of current parameter values in use. /// - protected virtual IReadOnlyDictionary ParameterValues { get; private set; } + protected virtual CacheSafeParameterFacade ParametersFacade { get; private set; } /// /// Processes a query expression to apply null semantics and optimize it. /// /// A query expression to process. - /// A dictionary of parameter values in use. - /// A bool value indicating whether the query expression can be cached. + /// A facade allowing access to parameters in a cache-safe way. /// An optimized query expression. - public virtual Expression Process( - Expression queryExpression, - IReadOnlyDictionary parameterValues, - out bool canCache) + public virtual Expression Process(Expression queryExpression, CacheSafeParameterFacade parametersFacade) { - _canCache = true; _nonNullableColumns.Clear(); _nullValueColumns.Clear(); - ParameterValues = parameterValues; + _parametersForValues.Clear(); + ParametersFacade = parametersFacade; var result = Visit(queryExpression); - canCache = _canCache; - return result; } - /// - /// Marks the select expression being processed as cannot be cached. - /// - protected virtual void DoNotCache() - => _canCache = false; - /// /// Adds a column to non nullable columns list to further optimizations can take the column as non-nullable. /// @@ -115,27 +112,47 @@ protected override Expression VisitExtension(Expression node) case ValuesExpression { ValuesParameter: SqlParameterExpression valuesParameter } valuesExpression: { - DoNotCache(); Check.DebugAssert(valuesParameter.TypeMapping is not null, "valuesParameter.TypeMapping is not null"); Check.DebugAssert( valuesParameter.TypeMapping.ElementTypeMapping is not null, "valuesParameter.TypeMapping.ElementTypeMapping is not null"); var typeMapping = (RelationalTypeMapping)valuesParameter.TypeMapping.ElementTypeMapping; - var values = (IEnumerable?)ParameterValues[valuesParameter.Name] ?? Array.Empty(); + var queryParameters = ParametersFacade.GetParametersAndDisableSqlCaching(); + var values = ((IEnumerable?)queryParameters[valuesParameter.Name])?.Cast().ToList() ?? []; var processedValues = new List(); - foreach (var value in values) + + if (!valuesParameter.ShouldBeConstantized + && (ParameterizedCollectionTranslationMode is null or PCTM.ParameterizeExpanded)) + { + var parameters = _parametersForValues.GetOrAddNew(valuesParameter); + for (var i = 0; i < values.Count; i++) + { + // Create parameter for value if we didn't create it yet, + // otherwise reuse it. + if (parameters.Count <= i) + { + var parameterName = Uniquifier.Uniquify(valuesParameter.Name, queryParameters, int.MaxValue); + queryParameters.Add(parameterName, values[i]); + var parameterExpression = new SqlParameterExpression(parameterName, values[i]?.GetType() ?? typeof(object), typeMapping); + parameters.Add(parameterExpression); + } + processedValues.Add(new RowValueExpression([parameters[i]])); + } + } + else { - processedValues.Add( - new RowValueExpression( - [ - _sqlExpressionFactory.Constant(value, value?.GetType() ?? typeof(object), sensitive: true, typeMapping) - ])); + foreach (var value in values) + { + processedValues.Add( + new RowValueExpression( + [ + _sqlExpressionFactory.Constant(value, value?.GetType() ?? typeof(object), sensitive: true, typeMapping) + ])); + } } - return processedValues is not [] - ? valuesExpression.Update(processedValues) - : valuesExpression; + return valuesExpression.Update(ProcessValuesOrderingColumn(valuesExpression, processedValues)); } default: @@ -176,6 +193,36 @@ or ExpressionType.LessThan } } + /// + /// If we still have _ord column here (it was not removed by other optimizations), + /// we need to add value for it. + /// + /// Expression where to look for ordering column. + /// Row values to process. + /// Row values with ordering, if needed. + protected virtual List ProcessValuesOrderingColumn(ValuesExpression valuesExpression, List values) + { + if (valuesExpression.ColumnNames[0] != RelationalQueryableMethodTranslatingExpressionVisitor.ValuesOrderingColumnName) + { + return values; + } + + var result = new List(values.Count); + var cnt = 1; + var intTypeMapping = Dependencies.TypeMappingSource.FindMapping(typeof(int)); + + foreach (var item in values) + { + result.Add(new RowValueExpression( + [ + _sqlExpressionFactory.Constant(cnt++, intTypeMapping), + .. item.Values, + ])); + } + + return result; + } + /// /// Visits a . /// @@ -503,16 +550,31 @@ protected virtual SqlExpression VisitIn(InExpression inExpression, bool allowOpt } var projectionExpression = Visit(subqueryProjection, allowOptimizedExpansion, out var projectionNullable); - inExpression = inExpression.Update( - item, subquery.Update( - subquery.Tables, - subquery.Predicate, - subquery.GroupBy, - subquery.Having, - projections: [subquery.Projection[0].Update(projectionExpression)], - subquery.Orderings, - subquery.Offset, - subquery.Limit)); + if (subquery is { Tables: [ValuesExpression { RowValues: { } rowValues }] }) + { + inExpression = inExpression.Update( + item, + [.. rowValues + // Remove explicit cast from RelationalTypeMappingPostprocessor.ApplyTypeMappingsOnValuesExpression. + // For IN it is not needed. + .Select(r => r.Values[0] is SqlUnaryExpression { OperatorType: ExpressionType.Convert } convert + ? convert.Operand + : r.Values[0])]); + return VisitIn(inExpression, allowOptimizedExpansion, out nullable); + } + else + { + inExpression = inExpression.Update( + item, subquery.Update( + subquery.Tables, + subquery.Predicate, + subquery.GroupBy, + subquery.Having, + projections: [subquery.Projection[0].Update(projectionExpression)], + subquery.Orderings, + subquery.Offset, + subquery.Limit)); + } if (UseRelationalNulls) { @@ -548,7 +610,7 @@ protected virtual SqlExpression VisitIn(InExpression inExpression, bool allowOpt case (false, true): { - // If the item is non-nullable but the projection is nullable, NULL will only be returned if the item wasn't found + // If the item is non-nullable but the subquery projection is nullable, NULL will only be returned if the item wasn't found // (as with the above case). // Use as-is in optimized expansion (NULL is interpreted as false anyway), or compensate by coalescing NULL to false: // WHERE NonNullable IN (SELECT Nullable FROM foo) -> WHERE COALESCE(NonNullable IN (SELECT Nullable FROM foo), false) @@ -751,12 +813,14 @@ InExpression ProcessInExpressionValues( { // The InExpression has a values parameter. Expand it out, embedding its values as constants into the SQL; disable SQL // caching. - DoNotCache(); + var parameters = ParametersFacade.GetParametersAndDisableSqlCaching(); + var values = (IEnumerable?)parameters[valuesParameter.Name] ?? Array.Empty(); var typeMapping = inExpression.ValuesParameter.TypeMapping; - var values = (IEnumerable?)ParameterValues[valuesParameter.Name] ?? Array.Empty(); processedValues = []; + var useParameters = !valuesParameter.ShouldBeConstantized + && (ParameterizedCollectionTranslationMode is null or PCTM.ParameterizeExpanded); foreach (var value in values) { if (value is null && removeNulls) @@ -765,7 +829,16 @@ InExpression ProcessInExpressionValues( continue; } - processedValues.Add(_sqlExpressionFactory.Constant(value, value?.GetType() ?? typeof(object), sensitive: true, typeMapping)); + if (useParameters) + { + var parameterName = Uniquifier.Uniquify(valuesParameter.Name, parameters, int.MaxValue); + parameters.Add(parameterName, value); + processedValues.Add(new SqlParameterExpression(parameterName, value?.GetType() ?? typeof(object), typeMapping)); + } + else + { + processedValues.Add(_sqlExpressionFactory.Constant(value, value?.GetType() ?? typeof(object), sensitive: true, typeMapping)); + } } } else @@ -1306,28 +1379,32 @@ protected virtual SqlExpression VisitSqlParameter( bool allowOptimizedExpansion, out bool nullable) { - if (!ParameterValues.TryGetValue(sqlParameterExpression.Name, out var parameterValue)) - { - throw new UnreachableException( - $"Encountered SqlParameter with name '{sqlParameterExpression.Name}', but such a parameter does not exist."); - } + // if (!ParametersFacade.TryGetValue(sqlParameterExpression.Name, out var parameterValue)) + // { + // throw new UnreachableException( + // $"Encountered SqlParameter with name '{sqlParameterExpression.Name}', but such a parameter does not exist."); + // } - nullable = parameterValue == null; + // nullable = parameterValue == null; - if (nullable) + if (ParametersFacade.IsParameterNull(sqlParameterExpression.Name)) { + nullable = true; + return _sqlExpressionFactory.Constant( null, sqlParameterExpression.Type, sqlParameterExpression.TypeMapping); } + nullable = false; + if (sqlParameterExpression.ShouldBeConstantized) { - DoNotCache(); + var parameters = ParametersFacade.GetParametersAndDisableSqlCaching(); return _sqlExpressionFactory.Constant( - parameterValue, + parameters[sqlParameterExpression.Name], sqlParameterExpression.Type, sensitive: true, sqlParameterExpression.TypeMapping); @@ -1405,7 +1482,7 @@ protected virtual bool PreferExistsToInWithCoalesce // Note that we can check parameter values for null since we cache by the parameter nullability; but we cannot do the same for bool. private bool IsNull(SqlExpression? expression) => expression is SqlConstantExpression { Value: null } - || expression is SqlParameterExpression { Name: string parameterName } && ParameterValues[parameterName] is null; + || expression is SqlParameterExpression { Name: string parameterName } && ParametersFacade.IsParameterNull(parameterName); private bool IsTrue(SqlExpression? expression) => expression is SqlConstantExpression { Value: true }; @@ -1703,11 +1780,14 @@ protected virtual bool TryMakeNonNullable( } && projectedColumn.TableAlias == collectionTable.Alias && IsCollectionTable(collectionTable, out var collection) - && collection is SqlParameterExpression collectionParameter - && ParameterValues[collectionParameter.Name] is IList values) + && collection is SqlParameterExpression collectionParameter) { - // We're looking at a parameter beyond its simple nullability, so we can't use the 2nd-level cache for this query. - DoNotCache(); + // We're looking at a parameter beyond its simple nullability, so we can't use the SQL cache for this query. + var parameters = ParametersFacade.GetParametersAndDisableSqlCaching(); + if (parameters[collectionParameter.Name] is not IList values) + { + throw new UnreachableException($"Parameter '{collectionParameter.Name}' is not an IList."); + } IList? processedValues = null; @@ -1745,20 +1825,9 @@ protected virtual bool TryMakeNonNullable( foundNull = true; - // TODO: We currently only have read-only access to the parameter values in the nullability processor (and in all of the - // 2nd-level query pipeline); to need to flow the mutable dictionary in. Note that any modification of parameter values (as - // here) must immediately entail DoNotCache(). - Check.DebugAssert(ParameterValues is Dictionary, "ParameterValues isn't a Dictionary"); - if (ParameterValues is not Dictionary mutableParameterValues) - { - rewrittenSelectExpression = null; - foundNull = null; - return false; - } - var rewrittenParameter = new SqlParameterExpression( collectionParameter.Name + "_without_nulls", collectionParameter.Type, collectionParameter.TypeMapping); - mutableParameterValues[rewrittenParameter.Name] = processedValues; + parameters[rewrittenParameter.Name] = processedValues; var rewrittenCollectionTable = UpdateParameterCollection(collectionTable, rewrittenParameter); // We clone the select expression since Update below doesn't create a pure copy, mutating the original as well (because of @@ -1840,7 +1909,7 @@ private SqlExpression ProcessNullNotNull(SqlExpression sqlExpression, bool opera // not_null_value_parameter is null -> false // not_null_value_parameter is not null -> true return _sqlExpressionFactory.Constant( - ParameterValues[sqlParameterOperand.Name] == null ^ sqlUnaryExpression.OperatorType == ExpressionType.NotEqual, + ParametersFacade.IsParameterNull(sqlParameterOperand.Name) ^ sqlUnaryExpression.OperatorType == ExpressionType.NotEqual, sqlUnaryExpression.TypeMapping); case ColumnExpression columnOperand diff --git a/src/EFCore.SqlServer/Query/Internal/SqlServerJsonPostprocessor.cs b/src/EFCore.SqlServer/Query/Internal/SqlServerJsonPostprocessor.cs index f523032f5cd..f0507306661 100644 --- a/src/EFCore.SqlServer/Query/Internal/SqlServerJsonPostprocessor.cs +++ b/src/EFCore.SqlServer/Query/Internal/SqlServerJsonPostprocessor.cs @@ -28,7 +28,7 @@ namespace Microsoft.EntityFrameworkCore.SqlServer.Query.Internal; public sealed class SqlServerJsonPostprocessor( IRelationalTypeMappingSource typeMappingSource, ISqlExpressionFactory sqlExpressionFactory, - SqlAliasManager sqlAliasManager) + SqlAliasManager? sqlAliasManager) : ExpressionVisitor { private readonly List _openjsonOuterAppliesToAdd = new(); @@ -229,6 +229,9 @@ when _columnsToRewrite.TryGetValue((columnExpression.TableAlias, columnExpressio ?? (jsonScalar.Json as ColumnExpression)?.Name ?? "Json"; + // We need to generaste alias here and hence need SqlAliasManager. + // Other code paths have already alias and don't need to access SqlAliasManager. + Check.DebugAssert(sqlAliasManager is not null, "No SqlAliasManager in SqlServerJsonPostprocessor"); var tableAlias = sqlAliasManager.GenerateTableAlias(name); var join = new OuterApplyExpression( diff --git a/src/EFCore.SqlServer/Query/Internal/SqlServerParameterBasedSqlProcessor.cs b/src/EFCore.SqlServer/Query/Internal/SqlServerParameterBasedSqlProcessor.cs index 94c15ba6c68..bfb214ddfe9 100644 --- a/src/EFCore.SqlServer/Query/Internal/SqlServerParameterBasedSqlProcessor.cs +++ b/src/EFCore.SqlServer/Query/Internal/SqlServerParameterBasedSqlProcessor.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 Microsoft.EntityFrameworkCore.SqlServer.Infrastructure.Internal; + namespace Microsoft.EntityFrameworkCore.SqlServer.Query.Internal; /// @@ -11,6 +13,8 @@ namespace Microsoft.EntityFrameworkCore.SqlServer.Query.Internal; /// public class SqlServerParameterBasedSqlProcessor : RelationalParameterBasedSqlProcessor { + private readonly ISqlServerSingletonOptions _sqlServerSingletonOptions; + /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to /// the same compatibility standards as public APIs. It may be changed or removed without notice in @@ -19,9 +23,11 @@ public class SqlServerParameterBasedSqlProcessor : RelationalParameterBasedSqlPr /// public SqlServerParameterBasedSqlProcessor( RelationalParameterBasedSqlProcessorDependencies dependencies, - RelationalParameterBasedSqlProcessorParameters parameters) + RelationalParameterBasedSqlProcessorParameters parameters, + ISqlServerSingletonOptions sqlServerSingletonOptions) : base(dependencies, parameters) { + _sqlServerSingletonOptions = sqlServerSingletonOptions; } /// @@ -30,31 +36,21 @@ public SqlServerParameterBasedSqlProcessor( /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// - public override Expression Optimize( - Expression queryExpression, - IReadOnlyDictionary parametersValues, - out bool canCache) + public override Expression Process(Expression queryExpression, CacheSafeParameterFacade parametersFacade) { - var optimizedQueryExpression = new SkipTakeCollapsingExpressionVisitor(Dependencies.SqlExpressionFactory) - .Process(queryExpression, parametersValues, out var canCache2); + var afterZeroLimitConversion = new SqlServerZeroLimitConverter(Dependencies.SqlExpressionFactory) + .Process(queryExpression, parametersFacade); - optimizedQueryExpression = base.Optimize(optimizedQueryExpression, parametersValues, out canCache); + var afterBaseProcessing = base.Process(afterZeroLimitConversion, parametersFacade); - canCache &= canCache2; + var afterSearchConditionConversion = new SearchConditionConverter(Dependencies.SqlExpressionFactory) + .Visit(afterBaseProcessing); - return new SearchConditionConverter(Dependencies.SqlExpressionFactory).Visit(optimizedQueryExpression); + return afterSearchConditionConversion; } /// - protected override Expression ProcessSqlNullability( - Expression selectExpression, - IReadOnlyDictionary parametersValues, - out bool canCache) - { - Check.NotNull(selectExpression, nameof(selectExpression)); - Check.NotNull(parametersValues, nameof(parametersValues)); - - return new SqlServerSqlNullabilityProcessor(Dependencies, Parameters).Process( - selectExpression, parametersValues, out canCache); - } + protected override Expression ProcessSqlNullability(Expression selectExpression, CacheSafeParameterFacade parametersFacade) + => new SqlServerSqlNullabilityProcessor(Dependencies, Parameters, _sqlServerSingletonOptions) + .Process(selectExpression, parametersFacade); } diff --git a/src/EFCore.SqlServer/Query/Internal/SqlServerParameterBasedSqlProcessorFactory.cs b/src/EFCore.SqlServer/Query/Internal/SqlServerParameterBasedSqlProcessorFactory.cs index 9d60b17dba7..79995b14649 100644 --- a/src/EFCore.SqlServer/Query/Internal/SqlServerParameterBasedSqlProcessorFactory.cs +++ b/src/EFCore.SqlServer/Query/Internal/SqlServerParameterBasedSqlProcessorFactory.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 Microsoft.EntityFrameworkCore.SqlServer.Infrastructure.Internal; + namespace Microsoft.EntityFrameworkCore.SqlServer.Query.Internal; /// @@ -11,6 +13,8 @@ namespace Microsoft.EntityFrameworkCore.SqlServer.Query.Internal; /// public class SqlServerParameterBasedSqlProcessorFactory : IRelationalParameterBasedSqlProcessorFactory { + private readonly ISqlServerSingletonOptions _sqlServerSingletonOptions; + /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to /// the same compatibility standards as public APIs. It may be changed or removed without notice in @@ -18,8 +22,12 @@ public class SqlServerParameterBasedSqlProcessorFactory : IRelationalParameterBa /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public SqlServerParameterBasedSqlProcessorFactory( - RelationalParameterBasedSqlProcessorDependencies dependencies) - => Dependencies = dependencies; + RelationalParameterBasedSqlProcessorDependencies dependencies, + ISqlServerSingletonOptions sqlServerSingletonOptions) + { + Dependencies = dependencies; + _sqlServerSingletonOptions = sqlServerSingletonOptions; + } /// /// Relational provider-specific dependencies for this service. @@ -33,5 +41,5 @@ public SqlServerParameterBasedSqlProcessorFactory( /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public virtual RelationalParameterBasedSqlProcessor Create(RelationalParameterBasedSqlProcessorParameters parameters) - => new SqlServerParameterBasedSqlProcessor(Dependencies, parameters); + => new SqlServerParameterBasedSqlProcessor(Dependencies, parameters, _sqlServerSingletonOptions); } diff --git a/src/EFCore.SqlServer/Query/Internal/SqlServerSqlNullabilityProcessor.cs b/src/EFCore.SqlServer/Query/Internal/SqlServerSqlNullabilityProcessor.cs index a3fb56ea225..6acb1667ea2 100644 --- a/src/EFCore.SqlServer/Query/Internal/SqlServerSqlNullabilityProcessor.cs +++ b/src/EFCore.SqlServer/Query/Internal/SqlServerSqlNullabilityProcessor.cs @@ -1,8 +1,11 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Collections; using System.Diagnostics.CodeAnalysis; using Microsoft.EntityFrameworkCore.Query.SqlExpressions; +using Microsoft.EntityFrameworkCore.SqlServer.Infrastructure.Internal; +using PCTM = Microsoft.EntityFrameworkCore.Internal.ParameterizedCollectionTranslationMode; namespace Microsoft.EntityFrameworkCore.SqlServer.Query.Internal; @@ -14,6 +17,8 @@ namespace Microsoft.EntityFrameworkCore.SqlServer.Query.Internal; /// public class SqlServerSqlNullabilityProcessor : SqlNullabilityProcessor { + private readonly ISqlServerSingletonOptions _sqlServerSingletonOptions; + /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to /// the same compatibility standards as public APIs. It may be changed or removed without notice in @@ -22,9 +27,11 @@ public class SqlServerSqlNullabilityProcessor : SqlNullabilityProcessor /// public SqlServerSqlNullabilityProcessor( RelationalParameterBasedSqlProcessorDependencies dependencies, - RelationalParameterBasedSqlProcessorParameters parameters) + RelationalParameterBasedSqlProcessorParameters parameters, + ISqlServerSingletonOptions sqlServerSingletonOptions) : base(dependencies, parameters) { + _sqlServerSingletonOptions = sqlServerSingletonOptions; } /// @@ -145,5 +152,80 @@ protected override TableExpressionBase UpdateParameterCollection( => table is SqlServerOpenJsonExpression { Arguments: [SqlParameterExpression] } openJsonExpression ? openJsonExpression.Update(newCollectionParameter, path: null) : base.UpdateParameterCollection(table, newCollectionParameter); + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + protected override Expression VisitExtension(Expression node) + { + switch (node) + { + case ValuesExpression { ValuesParameter: SqlParameterExpression valuesParameter } valuesExpression + when ParameterizedCollectionTranslationMode is null or PCTM.ParameterizeExpanded: + { + var parameters = ParametersFacade.GetParametersAndDisableSqlCaching(); + var values = ((IEnumerable?)parameters[valuesParameter.Name])?.Cast().ToList() ?? []; + + if (values.Count > 2098) + { + Check.DebugAssert(valuesParameter.TypeMapping is not null, "valuesParameter.TypeMapping is not null"); + Check.DebugAssert( + valuesParameter.TypeMapping.ElementTypeMapping is not null, + "valuesParameter.TypeMapping.ElementTypeMapping is not null"); + var typeMapping = (RelationalTypeMapping)valuesParameter.TypeMapping.ElementTypeMapping; + + var openJsonSupported = + (_sqlServerSingletonOptions.EngineType == SqlServerEngineType.SqlServer + && _sqlServerSingletonOptions.SqlServerCompatibilityLevel >= 130) + || + (_sqlServerSingletonOptions.EngineType == SqlServerEngineType.AzureSql + && _sqlServerSingletonOptions.AzureSqlCompatibilityLevel >= 130) + || + (_sqlServerSingletonOptions.EngineType == SqlServerEngineType.AzureSynapse); + + if (openJsonSupported) + { + var openJsonExpression = new SqlServerOpenJsonExpression( + valuesExpression.Alias, + valuesParameter, + columnInfos: + [ + new SqlServerOpenJsonExpression.ColumnInfo + { + Name = "value", + TypeMapping = typeMapping, + Path = [] + } + ]); + var jsonPostprocessor = new SqlServerJsonPostprocessor( + Dependencies.TypeMappingSource, + Dependencies.SqlExpressionFactory, + sqlAliasManager: null); + return jsonPostprocessor.Process(openJsonExpression); + } + else + { + var processedValues = new List(); + foreach (var value in values) + { + processedValues.Add( + new RowValueExpression( + [ + Dependencies.SqlExpressionFactory.Constant(value, value?.GetType() ?? typeof(object), sensitive: true, typeMapping) + ])); + } + return valuesExpression.Update(ProcessValuesOrderingColumn(valuesExpression, processedValues)); + } + } + return base.VisitExtension(node); + } + + default: + return base.VisitExtension(node); + } + } #pragma warning restore EF1001 } diff --git a/src/EFCore.SqlServer/Query/Internal/SqlServerSqlTranslatingExpressionVisitor.cs b/src/EFCore.SqlServer/Query/Internal/SqlServerSqlTranslatingExpressionVisitor.cs index 8e4edd47f06..8b1abfc068d 100644 --- a/src/EFCore.SqlServer/Query/Internal/SqlServerSqlTranslatingExpressionVisitor.cs +++ b/src/EFCore.SqlServer/Query/Internal/SqlServerSqlTranslatingExpressionVisitor.cs @@ -482,7 +482,7 @@ SqlExpression CharIndexGreaterThanZero() QueryContext queryContext, string baseParameterName, StartsEndsWithContains methodType) - => queryContext.ParameterValues[baseParameterName] switch + => queryContext.Parameters[baseParameterName] switch { null => null, diff --git a/src/EFCore.SqlServer/Query/Internal/SkipTakeCollapsingExpressionVisitor.cs b/src/EFCore.SqlServer/Query/Internal/SqlServerZeroLimitConverter.cs similarity index 68% rename from src/EFCore.SqlServer/Query/Internal/SkipTakeCollapsingExpressionVisitor.cs rename to src/EFCore.SqlServer/Query/Internal/SqlServerZeroLimitConverter.cs index 00ecaab9ee9..f4eea232d21 100644 --- a/src/EFCore.SqlServer/Query/Internal/SkipTakeCollapsingExpressionVisitor.cs +++ b/src/EFCore.SqlServer/Query/Internal/SqlServerZeroLimitConverter.cs @@ -11,12 +11,11 @@ namespace Microsoft.EntityFrameworkCore.SqlServer.Query.Internal; /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// -public class SkipTakeCollapsingExpressionVisitor : ExpressionVisitor +public class SqlServerZeroLimitConverter : ExpressionVisitor { private readonly ISqlExpressionFactory _sqlExpressionFactory; - private IReadOnlyDictionary _parameterValues; - private bool _canCache; + private CacheSafeParameterFacade _parametersFacade; /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -24,10 +23,10 @@ public class SkipTakeCollapsingExpressionVisitor : ExpressionVisitor /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// - public SkipTakeCollapsingExpressionVisitor(ISqlExpressionFactory sqlExpressionFactory) + public SqlServerZeroLimitConverter(ISqlExpressionFactory sqlExpressionFactory) { _sqlExpressionFactory = sqlExpressionFactory; - _parameterValues = null!; + _parametersFacade = null!; } /// @@ -36,19 +35,11 @@ public SkipTakeCollapsingExpressionVisitor(ISqlExpressionFactory sqlExpressionFa /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// - public virtual Expression Process( - Expression queryExpression, - IReadOnlyDictionary parametersValues, - out bool canCache) + public virtual Expression Process(Expression queryExpression, CacheSafeParameterFacade parametersFacade) { - _parameterValues = parametersValues; - _canCache = true; + _parametersFacade = parametersFacade; - var result = Visit(queryExpression); - - canCache = _canCache; - - return result; + return Visit(queryExpression); } /// @@ -59,11 +50,11 @@ public virtual Expression Process( /// protected override Expression VisitExtension(Expression extensionExpression) { + // SQL Server doesn't support 0 in the FETCH NEXT x ROWS ONLY clause. We use this clause when translating LINQ Take(), but + // only if there's also a Skip(), otherwise we translate to SQL TOP(x), which does allow 0. + // Check for this case, and replace with a false predicate (since no rows should be returned). if (extensionExpression is SelectExpression { Offset: not null, Limit: not null } selectExpression) { - // SQL Server doesn't support 0 in the FETCH NEXT x ROWS ONLY clause. We use this clause when translating LINQ Take(), but - // only if there's also a Skip(), otherwise we translate to SQL TOP(x), which does allow 0. - // Check for this case, and replace with a false predicate (since no rows should be returned). if (IsZero(selectExpression.Limit)) { return selectExpression.Update( @@ -72,25 +63,18 @@ protected override Expression VisitExtension(Expression extensionExpression) selectExpression.GroupBy, selectExpression.GroupBy.Count > 0 ? _sqlExpressionFactory.Constant(false) : null, selectExpression.Projection, - new List(0), + orderings: [], offset: null, limit: null); } bool IsZero(SqlExpression? sqlExpression) - { - switch (sqlExpression) + => sqlExpression switch { - case SqlConstantExpression { Value: int intValue }: - return intValue == 0; - case SqlParameterExpression parameter: - _canCache = false; - return _parameterValues[parameter.Name] is 0; - - default: - return false; - } - } + SqlConstantExpression { Value: int i } => i == 0, + SqlParameterExpression p => _parametersFacade.GetParametersAndDisableSqlCaching()[p.Name] is 0, + _ => false + }; } return base.VisitExtension(extensionExpression); diff --git a/src/EFCore.Sqlite.Core/Query/Internal/SqliteParameterBasedSqlProcessor.cs b/src/EFCore.Sqlite.Core/Query/Internal/SqliteParameterBasedSqlProcessor.cs index 122c90235ee..3e62bf8e9a4 100644 --- a/src/EFCore.Sqlite.Core/Query/Internal/SqliteParameterBasedSqlProcessor.cs +++ b/src/EFCore.Sqlite.Core/Query/Internal/SqliteParameterBasedSqlProcessor.cs @@ -32,7 +32,7 @@ public SqliteParameterBasedSqlProcessor( /// protected override Expression ProcessSqlNullability( Expression queryExpression, - IReadOnlyDictionary parametersValues, + Dictionary parametersValues, out bool canCache) => new SqliteSqlNullabilityProcessor(Dependencies, Parameters).Process(queryExpression, parametersValues, out canCache); } diff --git a/src/EFCore.Sqlite.Core/Query/Internal/SqliteQuerySqlGenerator.cs b/src/EFCore.Sqlite.Core/Query/Internal/SqliteQuerySqlGenerator.cs index e14d97b1eff..952c270cecd 100644 --- a/src/EFCore.Sqlite.Core/Query/Internal/SqliteQuerySqlGenerator.cs +++ b/src/EFCore.Sqlite.Core/Query/Internal/SqliteQuerySqlGenerator.cs @@ -103,14 +103,19 @@ protected override void GenerateSetOperationOperand(SetOperationBase setOperatio // however, we can instead wrap the nested set operation in a SELECT * FROM () to achieve the same effect. // The following is a copy-paste of the base implementation from QuerySqlGenerator, adding the SELECT. - // INTERSECT has higher precedence over UNION and EXCEPT, but otherwise evaluation is left-to-right. - // To preserve evaluation order, add parentheses whenever a set operation is nested within a different set operation - // - including different distinctness. - // In addition, EXCEPT is non-commutative (unlike UNION/INTERSECT), so add parentheses for that case too (see #36105). - if (TryUnwrapBareSetOperation(operand, out var nestedSetOperation) - && (nestedSetOperation is ExceptExpression - || nestedSetOperation.GetType() != setOperation.GetType() - || nestedSetOperation.IsDistinct != setOperation.IsDistinct)) + if ( + // INTERSECT has higher precedence over UNION and EXCEPT, but otherwise evaluation is left-to-right. + // To preserve evaluation order, add parentheses whenever a set operation is nested within a different set operation + // - including different distinctness. + // In addition, EXCEPT is non-commutative (unlike UNION/INTERSECT), so add parentheses for that case too (see #36105). + (TryUnwrapBareSetOperation(operand, out var nestedSetOperation) + && (nestedSetOperation is ExceptExpression + || nestedSetOperation.GetType() != setOperation.GetType() + || nestedSetOperation.IsDistinct != setOperation.IsDistinct)) + || + // ValuesExpression with multiple rows uses UNION ALL by default. + // We wrap it in a SELECT * FROM () to ensure that the rows are treated as a single set operation. + (operand is { Tables: [ValuesExpression { RowValues.Count: > 1 }] })) { Sql.AppendLine("SELECT * FROM ("); diff --git a/src/EFCore.Sqlite.Core/Query/Internal/SqliteQueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore.Sqlite.Core/Query/Internal/SqliteQueryableMethodTranslatingExpressionVisitor.cs index b841caf5bc6..f3114cb2f8b 100644 --- a/src/EFCore.Sqlite.Core/Query/Internal/SqliteQueryableMethodTranslatingExpressionVisitor.cs +++ b/src/EFCore.Sqlite.Core/Query/Internal/SqliteQueryableMethodTranslatingExpressionVisitor.cs @@ -1,6 +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 System.Diagnostics.CodeAnalysis; using Microsoft.Data.Sqlite; using Microsoft.EntityFrameworkCore.Query.SqlExpressions; using Microsoft.EntityFrameworkCore.Sqlite.Internal; @@ -469,29 +470,62 @@ protected override ShapedQueryExpression TransformJsonQueryToTable(JsonQueryExpr Expression index, bool returnDefault) { - if (!returnDefault - && source.QueryExpression is SelectExpression + if (!returnDefault) + { + switch (source.QueryExpression) { - Tables: + // index on parameter using a column + // translate via JSON because SQLite can't use columns in OFFSET + case SelectExpression + { + Tables: [ValuesExpression { ValuesParameter: { } valuesParameter }], + Predicate: null, + GroupBy: [], + Having: null, + IsDistinct: false, +#pragma warning disable EF1001 + Orderings: [{ Expression: ColumnExpression { Name: ValuesOrderingColumnName }, IsAscending: true }], +#pragma warning restore EF1001 + Limit: null, + Offset: null + } selectExpression + when TranslateExpression(index) is { } translatedIndex + && TryTranslate(selectExpression, valuesParameter, translatedIndex, out var result): + return result; + + // Index on JSON array + case SelectExpression + { + Tables: [ TableValuedFunctionExpression { Name: "json_each", Schema: null, IsBuiltIn: true, Arguments: [var jsonArrayColumn] } jsonEachExpression ], - Predicate: null, - GroupBy: [], - Having: null, - IsDistinct: false, - Orderings: [{ Expression: ColumnExpression { Name: JsonEachKeyColumnName } orderingColumn, IsAscending: true }], - Limit: null, - Offset: null - } selectExpression - && orderingColumn.TableAlias == jsonEachExpression.Alias - && TranslateExpression(index) is { } translatedIndex) - { - // Index on JSON array + Predicate: null, + GroupBy: [], + Having: null, + IsDistinct: false, + Orderings: [{ Expression: ColumnExpression { Name: JsonEachKeyColumnName } orderingColumn, IsAscending: true }], + Limit: null, + Offset: null + } selectExpression + when orderingColumn.TableAlias == jsonEachExpression.Alias + && TranslateExpression(index) is { } translatedIndex + && TryTranslate(selectExpression, jsonArrayColumn, translatedIndex, out var result): + return result; + } + } + + return base.TranslateElementAtOrDefault(source, index, returnDefault); + bool TryTranslate( + SelectExpression selectExpression, + SqlExpression json, + SqlExpression translatedIndex, + [NotNullWhen(true)]out ShapedQueryExpression? result) + { // Extract the column projected out of the source, and simplify the subquery to a simple JsonScalarExpression var shaperExpression = source.ShaperExpression; if (shaperExpression is UnaryExpression { NodeType: ExpressionType.Convert } unaryExpression @@ -505,7 +539,7 @@ protected override ShapedQueryExpression TransformJsonQueryToTable(JsonQueryExpr && selectExpression.GetProjection(projectionBindingExpression) is ColumnExpression projectionColumn) { SqlExpression translation = new JsonScalarExpression( - jsonArrayColumn, + json, new[] { new PathSegment(translatedIndex) }, projectionColumn.Type, projectionColumn.TypeMapping, @@ -520,12 +554,14 @@ protected override ShapedQueryExpression TransformJsonQueryToTable(JsonQueryExpr } #pragma warning disable EF1001 - return source.UpdateQueryExpression(new SelectExpression(translation, _sqlAliasManager)); + result = source.UpdateQueryExpression(new SelectExpression(translation, _sqlAliasManager)); #pragma warning restore EF1001 + return true; } - } - return base.TranslateElementAtOrDefault(source, index, returnDefault); + result = default; + return false; + } } /// diff --git a/src/EFCore/Query/Internal/CompiledQueryBase.cs b/src/EFCore/Query/Internal/CompiledQueryBase.cs index 609f5f0e50b..a7c8c478d3f 100644 --- a/src/EFCore/Query/Internal/CompiledQueryBase.cs +++ b/src/EFCore/Query/Internal/CompiledQueryBase.cs @@ -67,9 +67,10 @@ protected virtual TResult ExecuteCore( queryContext.CancellationToken = cancellationToken; + var queryParameters = queryContext.Parameters; for (var i = 0; i < parameters.Length; i++) { - queryContext.AddParameter(_queryExpression.Parameters[i + 1].Name!, parameters[i]); + queryParameters.Add(_queryExpression.Parameters[i + 1].Name!, parameters[i]); } return _executor.Executor(queryContext); diff --git a/src/EFCore/Query/Internal/ExpressionTreeFuncletizer.cs b/src/EFCore/Query/Internal/ExpressionTreeFuncletizer.cs index e3788085ea8..44fdec7dbc0 100644 --- a/src/EFCore/Query/Internal/ExpressionTreeFuncletizer.cs +++ b/src/EFCore/Query/Internal/ExpressionTreeFuncletizer.cs @@ -96,7 +96,7 @@ public class ExpressionTreeFuncletizer : ExpressionVisitor private IQueryProvider? _currentQueryProvider; private State _state; - private IParameterValues _parameterValues = null!; + private Dictionary _parameters = null!; private readonly IModel _model; private readonly ContextParameterReplacer _contextParameterReplacer; @@ -154,10 +154,10 @@ public ExpressionTreeFuncletizer( /// public virtual Expression ExtractParameters( Expression expression, - IParameterValues parameterValues, + Dictionary parameters, bool parameterize, bool clearParameterizedValues) - => ExtractParameters(expression, parameterValues, parameterize, clearParameterizedValues, precompiledQuery: false); + => ExtractParameters(expression, parameters, parameterize, clearParameterizedValues, precompiledQuery: false); /// /// Processes an expression tree, extracting parameters and evaluating evaluatable fragments as part of the pass. @@ -172,13 +172,13 @@ public virtual Expression ExtractParameters( [Experimental(EFDiagnostics.PrecompiledQueryExperimental)] public virtual Expression ExtractParameters( Expression expression, - IParameterValues parameterValues, + Dictionary parameters, bool parameterize, bool clearParameterizedValues, bool precompiledQuery) { Reset(clearParameterizedValues); - _parameterValues = parameterValues; + _parameters = parameters; _parameterize = parameterize; _calculatingPath = false; _precompiledQuery = precompiledQuery; @@ -210,7 +210,7 @@ public virtual void ResetPathCalculation() // In precompilation mode we don't actually extract parameter values; but we do need to generate the parameter names, using the // same logic (and via the same code) used in parameter extraction, and that logic requires _parameterValues. - _parameterValues = new DummyParameterValues(); + _parameters = new Dictionary(); } /// @@ -1955,7 +1955,7 @@ private static StateType CombineStateTypes(StateType stateType1, StateType state }; // We still maintain _parameterValues since later parameter names are generated based on already-populated names. - _parameterValues.AddParameter(parameterName, null); + _parameters.Add(parameterName, null); return evaluatableRoot; } @@ -1972,7 +1972,7 @@ private static StateType CombineStateTypes(StateType stateType1, StateType state && !evaluatableRoot.Type.IsValueType && evaluatableRoot is MemberExpression { Member: IParameterNullabilityInfo { IsNonNullableReferenceType: true } }; - _parameterValues.AddParameter(parameterName, value); + _parameters.Add(parameterName, value); return _parameterizedValues[evaluatableRoot] = new QueryParameterExpression( parameterName, @@ -2330,15 +2330,4 @@ private sealed class ContextParameterReplacer(Type contextType) : ExpressionVisi ? ContextParameterExpression : base.Visit(expression); } - - private sealed class DummyParameterValues : IParameterValues - { - private readonly Dictionary _parameterValues = new(); - - public IReadOnlyDictionary ParameterValues - => _parameterValues; - - public void AddParameter(string name, object? value) - => _parameterValues.Add(name, value); - } } diff --git a/src/EFCore/Query/Internal/IParameterValues.cs b/src/EFCore/Query/Internal/IParameterValues.cs deleted file mode 100644 index 8d52ce91f32..00000000000 --- a/src/EFCore/Query/Internal/IParameterValues.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace Microsoft.EntityFrameworkCore.Query.Internal; - -/// -/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to -/// the same compatibility standards as public APIs. It may be changed or removed without notice in -/// any release. You should only use it directly in your code with extreme caution and knowing that -/// doing so can result in application failures when updating to a new Entity Framework Core release. -/// -public interface IParameterValues -{ - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - IReadOnlyDictionary ParameterValues { get; } - - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - void AddParameter(string name, object? value); -} diff --git a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs index 00626411739..d304cd7019c 100644 --- a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs +++ b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs @@ -56,7 +56,7 @@ private static readonly PropertyInfo QueryContextContextPropertyInfo private readonly Dictionary _parameterizedQueryFilterPredicateCache = []; - private readonly Parameters _parameters = new(); + private readonly Dictionary _parameters = new(); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -168,7 +168,7 @@ public virtual Expression Expand(Expression query) QueryContextContextPropertyInfo), _queryCompilationContext.ContextType); - foreach (var (key, value) in _parameters.ParameterValues) + foreach (var (key, value) in _parameters) { var lambda = (LambdaExpression)value!; var remappedLambdaBody = ReplacingExpressionVisitor.Replace( @@ -2283,35 +2283,15 @@ private static Expression SnapshotExpression(Expression selector) } private static EntityReference? UnwrapEntityReference(Expression? expression) - { - switch (expression) + => expression switch { - case EntityReference entityReference: - return entityReference; - - case NavigationTreeExpression navigationTreeExpression: - return UnwrapEntityReference(navigationTreeExpression.Value); - - case NavigationExpansionExpression navigationExpansionExpression - when navigationExpansionExpression.CardinalityReducingGenericMethodInfo != null: - return UnwrapEntityReference(navigationExpansionExpression.PendingSelector); - - case OwnedNavigationReference ownedNavigationReference: - return ownedNavigationReference.EntityReference; + EntityReference entityReference => entityReference, + NavigationTreeExpression navigationTreeExpression => UnwrapEntityReference(navigationTreeExpression.Value), + NavigationExpansionExpression navigationExpansionExpression + when navigationExpansionExpression.CardinalityReducingGenericMethodInfo is not null + => UnwrapEntityReference(navigationExpansionExpression.PendingSelector), + OwnedNavigationReference ownedNavigationReference => ownedNavigationReference.EntityReference, - default: - return null; - } - } - - private sealed class Parameters : IParameterValues - { - private readonly IDictionary _parameterValues = new Dictionary(); - - public IReadOnlyDictionary ParameterValues - => (IReadOnlyDictionary)_parameterValues; - - public void AddParameter(string name, object? value) - => _parameterValues.Add(name, value); - } + _ => null, + }; } diff --git a/src/EFCore/Query/Internal/QueryCompiler.cs b/src/EFCore/Query/Internal/QueryCompiler.cs index 950c7bebde3..4476f54b6f5 100644 --- a/src/EFCore/Query/Internal/QueryCompiler.cs +++ b/src/EFCore/Query/Internal/QueryCompiler.cs @@ -74,7 +74,7 @@ private TResult ExecuteCore(Expression query, bool async, CancellationT queryContext.CancellationToken = cancellationToken; - var queryAfterExtraction = ExtractParameters(query, queryContext, _logger); + var queryAfterExtraction = ExtractParameters(query, queryContext.Parameters, _logger); var compiledQuery = _compiledQueryCache @@ -95,7 +95,9 @@ var compiledQuery /// public virtual Func CreateCompiledQuery(Expression query) { - var queryAfterExtraction = ExtractParameters(query, _queryContextFactory.Create(), _logger, compiledQuery: true); + var queryContext = _queryContextFactory.Create(); + + var queryAfterExtraction = ExtractParameters(query, queryContext.Parameters, _logger, compiledQuery: true); return CompileQueryCore(_database, queryAfterExtraction, _model, false); } @@ -108,7 +110,9 @@ public virtual Func CreateCompiledQuery(Expressi /// public virtual Func CreateCompiledAsyncQuery(Expression query) { - var queryAfterExtraction = ExtractParameters(query, _queryContextFactory.Create(), _logger, compiledQuery: true); + var queryContext = _queryContextFactory.Create(); + + var queryAfterExtraction = ExtractParameters(query, queryContext.Parameters, _logger, compiledQuery: true); return CompileQueryCore(_database, queryAfterExtraction, _model, true); } @@ -135,9 +139,10 @@ public virtual Func CompileQueryCore( [Experimental(EFDiagnostics.PrecompiledQueryExperimental)] public virtual Expression> PrecompileQuery(Expression query, bool async) { + var queryContext = _queryContextFactory.Create(); + query = new ExpressionTreeFuncletizer(_model, _evaluatableExpressionFilter, _contextType, generateContextAccessors: false, _logger) - .ExtractParameters( - query, _queryContextFactory.Create(), parameterize: true, clearParameterizedValues: true, precompiledQuery: true); + .ExtractParameters(query, queryContext.Parameters, parameterize: true, clearParameterizedValues: true, precompiledQuery: true); return _database.CompileQueryExpression(query, async); } @@ -150,10 +155,10 @@ public virtual Expression> PrecompileQuery( /// public virtual Expression ExtractParameters( Expression query, - IParameterValues parameterValues, + Dictionary parameters, IDiagnosticsLogger logger, bool compiledQuery = false, bool generateContextAccessors = false) => new ExpressionTreeFuncletizer(_model, _evaluatableExpressionFilter, _contextType, generateContextAccessors: false, logger) - .ExtractParameters(query, parameterValues, parameterize: !compiledQuery, clearParameterizedValues: true); + .ExtractParameters(query, parameters, parameterize: !compiledQuery, clearParameterizedValues: true); } diff --git a/src/EFCore/Query/QueryCompilationContext.cs b/src/EFCore/Query/QueryCompilationContext.cs index 177ce563251..88545f5ee40 100644 --- a/src/EFCore/Query/QueryCompilationContext.cs +++ b/src/EFCore/Query/QueryCompilationContext.cs @@ -258,14 +258,19 @@ private Expression InsertRuntimeParameters(Expression query) .Select( kv => Expression.Call( - QueryContextParameter, - QueryContextAddParameterMethodInfo, + Expression.Property( + QueryContextParameter, + QueryContextParametersProperty), + ParameterDictionaryAddMethod, Expression.Constant(kv.Key), Expression.Convert(Expression.Invoke(kv.Value, QueryContextParameter), typeof(object)))) .Append(query)); - private static readonly MethodInfo QueryContextAddParameterMethodInfo - = typeof(QueryContext).GetTypeInfo().GetDeclaredMethod(nameof(QueryContext.AddParameter))!; + private static readonly PropertyInfo QueryContextParametersProperty + = typeof(QueryContext).GetProperty(nameof(QueryContext.Parameters))!; + + private static readonly MethodInfo ParameterDictionaryAddMethod + = typeof(Dictionary).GetMethod(nameof(Dictionary.Add))!; [DebuggerDisplay("{Microsoft.EntityFrameworkCore.Query.ExpressionPrinter.Print(this), nq}")] private sealed class NotTranslatedExpressionType : Expression, IPrintableExpression diff --git a/src/EFCore/Query/QueryContext.cs b/src/EFCore/Query/QueryContext.cs index 9237d7ff653..f66888cff33 100644 --- a/src/EFCore/Query/QueryContext.cs +++ b/src/EFCore/Query/QueryContext.cs @@ -19,9 +19,8 @@ namespace Microsoft.EntityFrameworkCore.Query; /// See Implementation of database providers and extensions /// and How EF Core queries work for more information and examples. /// -public abstract class QueryContext : IParameterValues +public abstract class QueryContext { - private readonly IDictionary _parameterValues = new Dictionary(); private IStateManager? _stateManager; /// @@ -41,10 +40,15 @@ protected QueryContext(QueryContextDependencies dependencies) } /// - /// The current DbContext in using while executing the query. + /// The current in using while executing the query. /// public virtual DbContext Context { get; } + /// + /// The query parameter used in the query query. + /// + public virtual Dictionary Parameters { get; } = new(); + /// /// Dependencies for this service. /// @@ -94,20 +98,6 @@ public virtual IDiagnosticsLogger CommandLogg public virtual IDiagnosticsLogger QueryLogger => Dependencies.QueryLogger; - /// - /// The parameter values to use while executing the query. - /// - public virtual IReadOnlyDictionary ParameterValues - => (IReadOnlyDictionary)_parameterValues; - - /// - /// Adds a parameter to for this query. - /// - /// The name. - /// The value. - public virtual void AddParameter(string name, object? value) - => _parameterValues.Add(name, value); - /// /// Initializes the to be used with this QueryContext. /// diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/PrimitiveCollectionsQueryCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/Query/PrimitiveCollectionsQueryCosmosTest.cs index 7324f4480e4..6b09305e63d 100644 --- a/test/EFCore.Cosmos.FunctionalTests/Query/PrimitiveCollectionsQueryCosmosTest.cs +++ b/test/EFCore.Cosmos.FunctionalTests/Query/PrimitiveCollectionsQueryCosmosTest.cs @@ -899,6 +899,14 @@ public override async Task Parameter_collection_Count_with_column_predicate_with Assert.Equal(CoreStrings.EFConstantNotSupported, exception.Message); } + // nothing to test here + public override Task Parameter_collection_Count_with_huge_number_of_values(bool async) + => base.Parameter_collection_Count_with_huge_number_of_values(async); + + // nothing to test here + public override Task Parameter_collection_of_ints_Contains_int_with_huge_number_of_values(bool async) + => base.Parameter_collection_of_ints_Contains_int_with_huge_number_of_values(async); + public override Task Column_collection_of_ints_Contains(bool async) => CosmosTestHelpers.Instance.NoSyncTest( async, async a => diff --git a/test/EFCore.Relational.Specification.Tests/Query/NonSharedPrimitiveCollectionsQueryRelationalTestBase.cs b/test/EFCore.Relational.Specification.Tests/Query/NonSharedPrimitiveCollectionsQueryRelationalTestBase.cs index 91595f5bfc7..5f1bcb603eb 100644 --- a/test/EFCore.Relational.Specification.Tests/Query/NonSharedPrimitiveCollectionsQueryRelationalTestBase.cs +++ b/test/EFCore.Relational.Specification.Tests/Query/NonSharedPrimitiveCollectionsQueryRelationalTestBase.cs @@ -16,6 +16,8 @@ public override Task Array_of_byte() protected abstract DbContextOptionsBuilder SetTranslateParameterizedCollectionsToParameters(DbContextOptionsBuilder optionsBuilder); + protected abstract DbContextOptionsBuilder SetTranslateParameterizedCollectionsToExpandedParameters(DbContextOptionsBuilder optionsBuilder); + [ConditionalFact] public virtual async Task Column_collection_inside_json_owned_entity() { @@ -59,7 +61,7 @@ public virtual async Task Parameter_collection_Count_with_column_predicate_with_ } [ConditionalFact] - public virtual async Task Parameter_collection_of_ints_Contains_int_with_default_constants() + public virtual async Task Parameter_collection_Contains_with_default_constants() { var contextFactory = await InitializeAsync( onConfiguring: b => SetTranslateParameterizedCollectionsToConstants(b), @@ -101,7 +103,7 @@ public virtual async Task Parameter_collection_Count_with_column_predicate_with_ } [ConditionalFact] - public virtual async Task Parameter_collection_of_ints_Contains_int_with_default_constants_EF_Parameter() + public virtual async Task Parameter_collection_Contains_with_default_constants_EF_Parameter() { var contextFactory = await InitializeAsync( onConfiguring: b => SetTranslateParameterizedCollectionsToConstants(b), @@ -142,7 +144,7 @@ public virtual async Task Parameter_collection_Count_with_column_predicate_with_ } [ConditionalFact] - public virtual async Task Parameter_collection_of_ints_Contains_int_with_default_parameters() + public virtual async Task Parameter_collection_Contains_with_default_parameters() { var contextFactory = await InitializeAsync( onConfiguring: b => SetTranslateParameterizedCollectionsToParameters(b), @@ -183,7 +185,7 @@ public virtual async Task Parameter_collection_Count_with_column_predicate_with_ } [ConditionalFact] - public virtual async Task Parameter_collection_of_ints_Contains_int_with_default_parameters_EF_Constant() + public virtual async Task Parameter_collection_Contains_with_default_parameters_EF_Constant() { var contextFactory = await InitializeAsync( onConfiguring: b => SetTranslateParameterizedCollectionsToParameters(b), @@ -203,6 +205,47 @@ public virtual async Task Parameter_collection_of_ints_Contains_int_with_default Assert.Equivalent(new[] { 2 }, result); } + [ConditionalFact] + public virtual async Task Parameter_collection_Count_with_column_predicate_with_default_expanded_parameters() + { + var contextFactory = await InitializeAsync( + onConfiguring: b => SetTranslateParameterizedCollectionsToExpandedParameters(b), + seed: context => + { + context.AddRange( + new TestEntity { Id = 1 }, + new TestEntity { Id = 100 }); + return context.SaveChangesAsync(); + }); + + await using var context = contextFactory.CreateContext(); + + var ids = new[] { 2, 999 }; + var result = await context.Set().Where(c => ids.Count(i => i > c.Id) == 1).Select(x => x.Id).ToListAsync(); + Assert.Equivalent(new[] { 100 }, result); + } + + [ConditionalFact] + public virtual async Task Parameter_collection_Contains_with_default_expanded_parameters() + { + var contextFactory = await InitializeAsync( + onConfiguring: b => SetTranslateParameterizedCollectionsToExpandedParameters(b), + seed: context => + { + context.AddRange( + new TestEntity { Id = 1 }, + new TestEntity { Id = 2 }, + new TestEntity { Id = 100 }); + return context.SaveChangesAsync(); + }); + + await using var context = contextFactory.CreateContext(); + + var ints = new[] { 2, 999 }; + var result = await context.Set().Where(c => ints.Contains(c.Id)).Select(x => x.Id).ToListAsync(); + Assert.Equivalent(new[] { 2 }, result); + } + protected class TestOwner { public int Id { get; set; } diff --git a/test/EFCore.Specification.Tests/Query/PrimitiveCollectionsQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/PrimitiveCollectionsQueryTestBase.cs index 56c5f161467..6c4b2596051 100644 --- a/test/EFCore.Specification.Tests/Query/PrimitiveCollectionsQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/PrimitiveCollectionsQueryTestBase.cs @@ -8,6 +8,8 @@ namespace Microsoft.EntityFrameworkCore.Query; public abstract class PrimitiveCollectionsQueryTestBase(TFixture fixture) : QueryTestBase(fixture) where TFixture : PrimitiveCollectionsQueryTestBase.PrimitiveCollectionsQueryFixtureBase, new() { + public virtual int? NumberOfValuesForHugeParameterCollectionTests { get; } = null; + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Inline_collection_of_ints_Contains(bool async) @@ -674,6 +676,43 @@ public virtual Task Parameter_collection_Count_with_column_predicate_with_EF_Con ss => ss.Set().Where(c => ids.Count(i => i > c.Id) == 2)); } + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Parameter_collection_Count_with_huge_number_of_values(bool async) + { + if (NumberOfValuesForHugeParameterCollectionTests is null) + { + return Task.CompletedTask; + } + + var extra = Enumerable.Range(1000, (int)NumberOfValuesForHugeParameterCollectionTests); + var ids = new[] { 2, 999 }.Concat(extra).ToArray(); + + return AssertQuery( + async, + ss => ss.Set().Where(c => ids.Count(i => i > c.Id) > 0)); + } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual async Task Parameter_collection_of_ints_Contains_int_with_huge_number_of_values(bool async) + { + if (NumberOfValuesForHugeParameterCollectionTests is null) + { + return; + } + + var extra = Enumerable.Range(1000, (int)NumberOfValuesForHugeParameterCollectionTests); + var ints = new[] { 10, 999 }.Concat(extra).ToArray(); + + await AssertQuery( + async, + ss => ss.Set().Where(c => ints.Contains(c.Int))); + await AssertQuery( + async, + ss => ss.Set().Where(c => !ints.Contains(c.Int))); + } + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Column_collection_of_ints_Contains(bool async) diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/AdHocMiscellaneousQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/AdHocMiscellaneousQuerySqlServerTest.cs index 8d59ea02a37..eca044db288 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/AdHocMiscellaneousQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/AdHocMiscellaneousQuerySqlServerTest.cs @@ -548,14 +548,11 @@ public virtual async Task DateTime_Contains_with_smalldatetime_generates_correct AssertSql( """ -@testDateList='["2018-10-07T00:00:00"]' (Size = 4000) +@testDateList1='2018-10-07T00:00:00.0000000' (DbType = DateTime) SELECT [r].[Id], [r].[MyTime] FROM [ReproEntity] AS [r] -WHERE [r].[MyTime] IN ( - SELECT [t].[value] - FROM OPENJSON(@testDateList) WITH ([value] smalldatetime '$') AS [t] -) +WHERE [r].[MyTime] = @testDateList1 """); } @@ -722,54 +719,131 @@ public async Task Where_contains_DateTime_literals(bool async) AssertSql( """ -@dateTimes='["1970-09-03T12:00:00","1971-09-03T12:00:10.22","1972-09-03T12:00:10.333","1973-09-03T12:00:10","1974-09-03T12:00:10.5","1975-09-03T12:00:10.66","1976-09-03T12:00:10.777","1977-09-03T12:00:10.888","1978-09-03T12:00:10.999","1979-09-03T12:00:10.111","1980-09-03T12:00:10.222"]' (Size = 4000) -@dateTimes0='["1970-09-03T12:00:00","1971-09-03T12:00:10.22","1972-09-03T12:00:10.333","1973-09-03T12:00:10","1974-09-03T12:00:10.5","1975-09-03T12:00:10.66","1976-09-03T12:00:10.777","1977-09-03T12:00:10.888","1978-09-03T12:00:10.999","1979-09-03T12:00:10.111","1980-09-03T12:00:10.222"]' (Size = 4000) -@dateTimes1='["1970-09-03T12:00:00","1971-09-03T12:00:10.22","1972-09-03T12:00:10.333","1973-09-03T12:00:10","1974-09-03T12:00:10.5","1975-09-03T12:00:10.66","1976-09-03T12:00:10.777","1977-09-03T12:00:10.888","1978-09-03T12:00:10.999","1979-09-03T12:00:10.111","1980-09-03T12:00:10.222"]' (Size = 4000) -@dateTimes2='["1970-09-03T12:00:00","1971-09-03T12:00:10.22","1972-09-03T12:00:10.333","1973-09-03T12:00:10","1974-09-03T12:00:10.5","1975-09-03T12:00:10.66","1976-09-03T12:00:10.777","1977-09-03T12:00:10.888","1978-09-03T12:00:10.999","1979-09-03T12:00:10.111","1980-09-03T12:00:10.222"]' (Size = 4000) -@dateTimes3='["1970-09-03T12:00:00","1971-09-03T12:00:10.22","1972-09-03T12:00:10.333","1973-09-03T12:00:10","1974-09-03T12:00:10.5","1975-09-03T12:00:10.66","1976-09-03T12:00:10.777","1977-09-03T12:00:10.888","1978-09-03T12:00:10.999","1979-09-03T12:00:10.111","1980-09-03T12:00:10.222"]' (Size = 4000) -@dateTimes4='["1970-09-03T12:00:00","1971-09-03T12:00:10.22","1972-09-03T12:00:10.333","1973-09-03T12:00:10","1974-09-03T12:00:10.5","1975-09-03T12:00:10.66","1976-09-03T12:00:10.777","1977-09-03T12:00:10.888","1978-09-03T12:00:10.999","1979-09-03T12:00:10.111","1980-09-03T12:00:10.222"]' (Size = 4000) -@dateTimes5='["1970-09-03T12:00:00","1971-09-03T12:00:10.22","1972-09-03T12:00:10.333","1973-09-03T12:00:10","1974-09-03T12:00:10.5","1975-09-03T12:00:10.66","1976-09-03T12:00:10.777","1977-09-03T12:00:10.888","1978-09-03T12:00:10.999","1979-09-03T12:00:10.111","1980-09-03T12:00:10.222"]' (Size = 4000) -@dateTimes6='["1970-09-03T12:00:00","1971-09-03T12:00:10.22","1972-09-03T12:00:10.333","1973-09-03T12:00:10","1974-09-03T12:00:10.5","1975-09-03T12:00:10.66","1976-09-03T12:00:10.777","1977-09-03T12:00:10.888","1978-09-03T12:00:10.999","1979-09-03T12:00:10.111","1980-09-03T12:00:10.222"]' (Size = 4000) -@dateTimes7='["1970-09-03T12:00:00","1971-09-03T12:00:10.22","1972-09-03T12:00:10.333","1973-09-03T12:00:10","1974-09-03T12:00:10.5","1975-09-03T12:00:10.66","1976-09-03T12:00:10.777","1977-09-03T12:00:10.888","1978-09-03T12:00:10.999","1979-09-03T12:00:10.111","1980-09-03T12:00:10.222"]' (Size = 4000) -@dateTimes8='["1970-09-03T12:00:00","1971-09-03T12:00:10.22","1972-09-03T12:00:10.333","1973-09-03T12:00:10","1974-09-03T12:00:10.5","1975-09-03T12:00:10.66","1976-09-03T12:00:10.777","1977-09-03T12:00:10.888","1978-09-03T12:00:10.999","1979-09-03T12:00:10.111","1980-09-03T12:00:10.222"]' (Size = 4000) -@dateTimes9='["1970-09-03T12:00:00","1971-09-03T12:00:10.22","1972-09-03T12:00:10.333","1973-09-03T12:00:10","1974-09-03T12:00:10.5","1975-09-03T12:00:10.66","1976-09-03T12:00:10.777","1977-09-03T12:00:10.888","1978-09-03T12:00:10.999","1979-09-03T12:00:10.111","1980-09-03T12:00:10.222"]' (Size = 4000) +@dateTimes1='1970-09-03T12:00:00.0000000' (DbType = DateTime) +@dateTimes2='1971-09-03T12:00:10.2200000' (DbType = DateTime) +@dateTimes3='1972-09-03T12:00:10.3330000' (DbType = DateTime) +@dateTimes4='1973-09-03T12:00:10.0000000' (DbType = DateTime) +@dateTimes5='1974-09-03T12:00:10.5000000' (DbType = DateTime) +@dateTimes6='1975-09-03T12:00:10.6600000' (DbType = DateTime) +@dateTimes7='1976-09-03T12:00:10.7770000' (DbType = DateTime) +@dateTimes8='1977-09-03T12:00:10.8880000' (DbType = DateTime) +@dateTimes9='1978-09-03T12:00:10.9990000' (DbType = DateTime) +@dateTimes10='1979-09-03T12:00:10.1110000' (DbType = DateTime) +@dateTimes11='1980-09-03T12:00:10.2220000' (DbType = DateTime) +@dateTimes12='1970-09-03T12:00:00.0000000' (DbType = DateTime) +@dateTimes13='1971-09-03T12:00:10.2200000' (DbType = DateTime) +@dateTimes14='1972-09-03T12:00:10.3330000' (DbType = DateTime) +@dateTimes15='1973-09-03T12:00:10.0000000' (DbType = DateTime) +@dateTimes16='1974-09-03T12:00:10.5000000' (DbType = DateTime) +@dateTimes17='1975-09-03T12:00:10.6600000' (DbType = DateTime) +@dateTimes18='1976-09-03T12:00:10.7770000' (DbType = DateTime) +@dateTimes19='1977-09-03T12:00:10.8880000' (DbType = DateTime) +@dateTimes20='1978-09-03T12:00:10.9990000' (DbType = DateTime) +@dateTimes21='1979-09-03T12:00:10.1110000' (DbType = DateTime) +@dateTimes22='1980-09-03T12:00:10.2220000' (DbType = DateTime) +@dateTimes23='1970-09-03T12:00:00.0000000' +@dateTimes24='1971-09-03T12:00:10.2200000' +@dateTimes25='1972-09-03T12:00:10.3330000' +@dateTimes26='1973-09-03T12:00:10.0000000' +@dateTimes27='1974-09-03T12:00:10.5000000' +@dateTimes28='1975-09-03T12:00:10.6600000' +@dateTimes29='1976-09-03T12:00:10.7770000' +@dateTimes30='1977-09-03T12:00:10.8880000' +@dateTimes31='1978-09-03T12:00:10.9990000' +@dateTimes32='1979-09-03T12:00:10.1110000' +@dateTimes33='1980-09-03T12:00:10.2220000' +@dateTimes34='1970-09-03T12:00:00.0000000' +@dateTimes35='1971-09-03T12:00:10.2200000' +@dateTimes36='1972-09-03T12:00:10.3330000' +@dateTimes37='1973-09-03T12:00:10.0000000' +@dateTimes38='1974-09-03T12:00:10.5000000' +@dateTimes39='1975-09-03T12:00:10.6600000' +@dateTimes40='1976-09-03T12:00:10.7770000' +@dateTimes41='1977-09-03T12:00:10.8880000' +@dateTimes42='1978-09-03T12:00:10.9990000' +@dateTimes43='1979-09-03T12:00:10.1110000' +@dateTimes44='1980-09-03T12:00:10.2220000' +@dateTimes45='1970-09-03T12:00:00.0000000' (Scale = 1) +@dateTimes46='1971-09-03T12:00:10.2200000' (Scale = 1) +@dateTimes47='1972-09-03T12:00:10.3330000' (Scale = 1) +@dateTimes48='1973-09-03T12:00:10.0000000' (Scale = 1) +@dateTimes49='1974-09-03T12:00:10.5000000' (Scale = 1) +@dateTimes50='1975-09-03T12:00:10.6600000' (Scale = 1) +@dateTimes51='1976-09-03T12:00:10.7770000' (Scale = 1) +@dateTimes52='1977-09-03T12:00:10.8880000' (Scale = 1) +@dateTimes53='1978-09-03T12:00:10.9990000' (Scale = 1) +@dateTimes54='1979-09-03T12:00:10.1110000' (Scale = 1) +@dateTimes55='1980-09-03T12:00:10.2220000' (Scale = 1) +@dateTimes56='1970-09-03T12:00:00.0000000' (Scale = 2) +@dateTimes57='1971-09-03T12:00:10.2200000' (Scale = 2) +@dateTimes58='1972-09-03T12:00:10.3330000' (Scale = 2) +@dateTimes59='1973-09-03T12:00:10.0000000' (Scale = 2) +@dateTimes60='1974-09-03T12:00:10.5000000' (Scale = 2) +@dateTimes61='1975-09-03T12:00:10.6600000' (Scale = 2) +@dateTimes62='1976-09-03T12:00:10.7770000' (Scale = 2) +@dateTimes63='1977-09-03T12:00:10.8880000' (Scale = 2) +@dateTimes64='1978-09-03T12:00:10.9990000' (Scale = 2) +@dateTimes65='1979-09-03T12:00:10.1110000' (Scale = 2) +@dateTimes66='1980-09-03T12:00:10.2220000' (Scale = 2) +@dateTimes67='1970-09-03T12:00:00.0000000' (Scale = 3) +@dateTimes68='1971-09-03T12:00:10.2200000' (Scale = 3) +@dateTimes69='1972-09-03T12:00:10.3330000' (Scale = 3) +@dateTimes70='1973-09-03T12:00:10.0000000' (Scale = 3) +@dateTimes71='1974-09-03T12:00:10.5000000' (Scale = 3) +@dateTimes72='1975-09-03T12:00:10.6600000' (Scale = 3) +@dateTimes73='1976-09-03T12:00:10.7770000' (Scale = 3) +@dateTimes74='1977-09-03T12:00:10.8880000' (Scale = 3) +@dateTimes75='1978-09-03T12:00:10.9990000' (Scale = 3) +@dateTimes76='1979-09-03T12:00:10.1110000' (Scale = 3) +@dateTimes77='1980-09-03T12:00:10.2220000' (Scale = 3) +@dateTimes78='1970-09-03T12:00:00.0000000' (Scale = 4) +@dateTimes79='1971-09-03T12:00:10.2200000' (Scale = 4) +@dateTimes80='1972-09-03T12:00:10.3330000' (Scale = 4) +@dateTimes81='1973-09-03T12:00:10.0000000' (Scale = 4) +@dateTimes82='1974-09-03T12:00:10.5000000' (Scale = 4) +@dateTimes83='1975-09-03T12:00:10.6600000' (Scale = 4) +@dateTimes84='1976-09-03T12:00:10.7770000' (Scale = 4) +@dateTimes85='1977-09-03T12:00:10.8880000' (Scale = 4) +@dateTimes86='1978-09-03T12:00:10.9990000' (Scale = 4) +@dateTimes87='1979-09-03T12:00:10.1110000' (Scale = 4) +@dateTimes88='1980-09-03T12:00:10.2220000' (Scale = 4) +@dateTimes89='1970-09-03T12:00:00.0000000' (Scale = 5) +@dateTimes90='1971-09-03T12:00:10.2200000' (Scale = 5) +@dateTimes91='1972-09-03T12:00:10.3330000' (Scale = 5) +@dateTimes92='1973-09-03T12:00:10.0000000' (Scale = 5) +@dateTimes93='1974-09-03T12:00:10.5000000' (Scale = 5) +@dateTimes94='1975-09-03T12:00:10.6600000' (Scale = 5) +@dateTimes95='1976-09-03T12:00:10.7770000' (Scale = 5) +@dateTimes96='1977-09-03T12:00:10.8880000' (Scale = 5) +@dateTimes97='1978-09-03T12:00:10.9990000' (Scale = 5) +@dateTimes98='1979-09-03T12:00:10.1110000' (Scale = 5) +@dateTimes99='1980-09-03T12:00:10.2220000' (Scale = 5) +@dateTimes100='1970-09-03T12:00:00.0000000' (Scale = 6) +@dateTimes101='1971-09-03T12:00:10.2200000' (Scale = 6) +@dateTimes102='1972-09-03T12:00:10.3330000' (Scale = 6) +@dateTimes103='1973-09-03T12:00:10.0000000' (Scale = 6) +@dateTimes104='1974-09-03T12:00:10.5000000' (Scale = 6) +@dateTimes105='1975-09-03T12:00:10.6600000' (Scale = 6) +@dateTimes106='1976-09-03T12:00:10.7770000' (Scale = 6) +@dateTimes107='1977-09-03T12:00:10.8880000' (Scale = 6) +@dateTimes108='1978-09-03T12:00:10.9990000' (Scale = 6) +@dateTimes109='1979-09-03T12:00:10.1110000' (Scale = 6) +@dateTimes110='1980-09-03T12:00:10.2220000' (Scale = 6) +@dateTimes111='1970-09-03T12:00:00.0000000' (Scale = 7) +@dateTimes112='1971-09-03T12:00:10.2200000' (Scale = 7) +@dateTimes113='1972-09-03T12:00:10.3330000' (Scale = 7) +@dateTimes114='1973-09-03T12:00:10.0000000' (Scale = 7) +@dateTimes115='1974-09-03T12:00:10.5000000' (Scale = 7) +@dateTimes116='1975-09-03T12:00:10.6600000' (Scale = 7) +@dateTimes117='1976-09-03T12:00:10.7770000' (Scale = 7) +@dateTimes118='1977-09-03T12:00:10.8880000' (Scale = 7) +@dateTimes119='1978-09-03T12:00:10.9990000' (Scale = 7) +@dateTimes120='1979-09-03T12:00:10.1110000' (Scale = 7) +@dateTimes121='1980-09-03T12:00:10.2220000' (Scale = 7) SELECT [d].[Id], [d].[DateTime], [d].[DateTime2], [d].[DateTime2_0], [d].[DateTime2_1], [d].[DateTime2_2], [d].[DateTime2_3], [d].[DateTime2_4], [d].[DateTime2_5], [d].[DateTime2_6], [d].[DateTime2_7], [d].[SmallDateTime] FROM [Dates] AS [d] -WHERE [d].[SmallDateTime] IN ( - SELECT [d0].[value] - FROM OPENJSON(@dateTimes) WITH ([value] smalldatetime '$') AS [d0] -) AND [d].[DateTime] IN ( - SELECT [d1].[value] - FROM OPENJSON(@dateTimes0) WITH ([value] datetime '$') AS [d1] -) AND [d].[DateTime2] IN ( - SELECT [d2].[value] - FROM OPENJSON(@dateTimes1) WITH ([value] datetime2 '$') AS [d2] -) AND [d].[DateTime2_0] IN ( - SELECT [d3].[value] - FROM OPENJSON(@dateTimes2) WITH ([value] datetime2(0) '$') AS [d3] -) AND [d].[DateTime2_1] IN ( - SELECT [d4].[value] - FROM OPENJSON(@dateTimes3) WITH ([value] datetime2(1) '$') AS [d4] -) AND [d].[DateTime2_2] IN ( - SELECT [d5].[value] - FROM OPENJSON(@dateTimes4) WITH ([value] datetime2(2) '$') AS [d5] -) AND [d].[DateTime2_3] IN ( - SELECT [d6].[value] - FROM OPENJSON(@dateTimes5) WITH ([value] datetime2(3) '$') AS [d6] -) AND [d].[DateTime2_4] IN ( - SELECT [d7].[value] - FROM OPENJSON(@dateTimes6) WITH ([value] datetime2(4) '$') AS [d7] -) AND [d].[DateTime2_5] IN ( - SELECT [d8].[value] - FROM OPENJSON(@dateTimes7) WITH ([value] datetime2(5) '$') AS [d8] -) AND [d].[DateTime2_6] IN ( - SELECT [d9].[value] - FROM OPENJSON(@dateTimes8) WITH ([value] datetime2(6) '$') AS [d9] -) AND [d].[DateTime2_7] IN ( - SELECT [d10].[value] - FROM OPENJSON(@dateTimes9) WITH ([value] datetime2(7) '$') AS [d10] -) +WHERE [d].[SmallDateTime] IN (@dateTimes1, @dateTimes2, @dateTimes3, @dateTimes4, @dateTimes5, @dateTimes6, @dateTimes7, @dateTimes8, @dateTimes9, @dateTimes10, @dateTimes11) AND [d].[DateTime] IN (@dateTimes12, @dateTimes13, @dateTimes14, @dateTimes15, @dateTimes16, @dateTimes17, @dateTimes18, @dateTimes19, @dateTimes20, @dateTimes21, @dateTimes22) AND [d].[DateTime2] IN (@dateTimes23, @dateTimes24, @dateTimes25, @dateTimes26, @dateTimes27, @dateTimes28, @dateTimes29, @dateTimes30, @dateTimes31, @dateTimes32, @dateTimes33) AND [d].[DateTime2_0] IN (@dateTimes34, @dateTimes35, @dateTimes36, @dateTimes37, @dateTimes38, @dateTimes39, @dateTimes40, @dateTimes41, @dateTimes42, @dateTimes43, @dateTimes44) AND [d].[DateTime2_1] IN (@dateTimes45, @dateTimes46, @dateTimes47, @dateTimes48, @dateTimes49, @dateTimes50, @dateTimes51, @dateTimes52, @dateTimes53, @dateTimes54, @dateTimes55) AND [d].[DateTime2_2] IN (@dateTimes56, @dateTimes57, @dateTimes58, @dateTimes59, @dateTimes60, @dateTimes61, @dateTimes62, @dateTimes63, @dateTimes64, @dateTimes65, @dateTimes66) AND [d].[DateTime2_3] IN (@dateTimes67, @dateTimes68, @dateTimes69, @dateTimes70, @dateTimes71, @dateTimes72, @dateTimes73, @dateTimes74, @dateTimes75, @dateTimes76, @dateTimes77) AND [d].[DateTime2_4] IN (@dateTimes78, @dateTimes79, @dateTimes80, @dateTimes81, @dateTimes82, @dateTimes83, @dateTimes84, @dateTimes85, @dateTimes86, @dateTimes87, @dateTimes88) AND [d].[DateTime2_5] IN (@dateTimes89, @dateTimes90, @dateTimes91, @dateTimes92, @dateTimes93, @dateTimes94, @dateTimes95, @dateTimes96, @dateTimes97, @dateTimes98, @dateTimes99) AND [d].[DateTime2_6] IN (@dateTimes100, @dateTimes101, @dateTimes102, @dateTimes103, @dateTimes104, @dateTimes105, @dateTimes106, @dateTimes107, @dateTimes108, @dateTimes109, @dateTimes110) AND [d].[DateTime2_7] IN (@dateTimes111, @dateTimes112, @dateTimes113, @dateTimes114, @dateTimes115, @dateTimes116, @dateTimes117, @dateTimes118, @dateTimes119, @dateTimes120, @dateTimes121) """); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/AdHocQueryFiltersQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/AdHocQueryFiltersQuerySqlServerTest.cs index 4b4e8478108..d2e44796c43 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/AdHocQueryFiltersQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/AdHocQueryFiltersQuerySqlServerTest.cs @@ -30,14 +30,12 @@ public override async Task Named_query_filters_anonymous() AssertSql( """ -@ef_filter___ids='[1,7]' (Size = 4000) +@ef_filter___ids1='1' +@ef_filter___ids2='7' SELECT [e].[Id], [e].[IsDeleted], [e].[IsDraft], [e].[Name] FROM [Entities] AS [e] -WHERE [e].[Id] NOT IN ( - SELECT [e0].[value] - FROM OPENJSON(@ef_filter___ids) WITH ([value] int '$') AS [e0] -) +WHERE [e].[Id] NOT IN (@ef_filter___ids1, @ef_filter___ids2) """); } @@ -217,14 +215,12 @@ public override async Task Query_filter_with_contains_evaluates_correctly() AssertSql( """ -@ef_filter___ids='[1,7]' (Size = 4000) +@ef_filter___ids1='1' +@ef_filter___ids2='7' SELECT [e].[Id], [e].[Name] FROM [Entities] AS [e] -WHERE [e].[Id] NOT IN ( - SELECT [e0].[value] - FROM OPENJSON(@ef_filter___ids) WITH ([value] int '$') AS [e0] -) +WHERE [e].[Id] NOT IN (@ef_filter___ids1, @ef_filter___ids2) """); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsCollectionsQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsCollectionsQuerySqlServerTest.cs index c3cfee30c00..4a123693a67 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsCollectionsQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsCollectionsQuerySqlServerTest.cs @@ -1216,7 +1216,8 @@ public override async Task LeftJoin_with_Any_on_outer_source_and_projecting_coll AssertSql( """ -@validIds='["L1 01","L1 02"]' (Size = 4000) +@validIds1='L1 01' (Size = 4000) +@validIds2='L1 02' (Size = 4000) SELECT CASE WHEN [l0].[Id] IS NULL THEN 0 @@ -1225,10 +1226,7 @@ ELSE [l0].[Id] FROM [LevelOne] AS [l] LEFT JOIN [LevelTwo] AS [l0] ON [l].[Id] = [l0].[Level1_Required_Id] LEFT JOIN [LevelThree] AS [l1] ON [l0].[Id] = [l1].[OneToMany_Required_Inverse3Id] -WHERE [l].[Name] IN ( - SELECT [v].[value] - FROM OPENJSON(@validIds) WITH ([value] nvarchar(max) '$') AS [v] -) +WHERE [l].[Name] IN (@validIds1, @validIds2) ORDER BY [l].[Id], [l0].[Id] """); } @@ -2332,25 +2330,20 @@ public override async Task Collection_projection_over_GroupBy_over_parameter(boo AssertSql( """ -@validIds='["L1 01","L1 02"]' (Size = 4000) +@validIds1='L1 01' (Size = 4000) +@validIds2='L1 02' (Size = 4000) SELECT [l1].[Date], [l2].[Id] FROM ( SELECT [l].[Date] FROM [LevelOne] AS [l] - WHERE [l].[Name] IN ( - SELECT [v].[value] - FROM OPENJSON(@validIds) WITH ([value] nvarchar(max) '$') AS [v] - ) + WHERE [l].[Name] IN (@validIds1, @validIds2) GROUP BY [l].[Date] ) AS [l1] LEFT JOIN ( SELECT [l0].[Id], [l0].[Date] FROM [LevelOne] AS [l0] - WHERE [l0].[Name] IN ( - SELECT [v0].[value] - FROM OPENJSON(@validIds) WITH ([value] nvarchar(max) '$') AS [v0] - ) + WHERE [l0].[Name] IN (@validIds1, @validIds2) ) AS [l2] ON [l1].[Date] = [l2].[Date] ORDER BY [l1].[Date] """); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsCollectionsSharedTypeQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsCollectionsSharedTypeQuerySqlServerTest.cs index 7b7307b6702..e16f2167ea3 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsCollectionsSharedTypeQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsCollectionsSharedTypeQuerySqlServerTest.cs @@ -2846,7 +2846,8 @@ public override async Task LeftJoin_with_Any_on_outer_source_and_projecting_coll AssertSql( """ -@validIds='["L1 01","L1 02"]' (Size = 4000) +@validIds1='L1 01' (Size = 4000) +@validIds2='L1 02' (Size = 4000) SELECT CASE WHEN [s].[OneToOne_Required_PK_Date] IS NULL OR [s].[Level1_Required_Id] IS NULL OR [s].[OneToMany_Required_Inverse2Id] IS NULL THEN 0 @@ -2872,10 +2873,7 @@ WHERE [l3].[Level2_Required_Id] IS NOT NULL AND [l3].[OneToMany_Required_Inverse ) AS [l4] ON CASE WHEN [s].[OneToOne_Required_PK_Date] IS NOT NULL AND [s].[Level1_Required_Id] IS NOT NULL AND [s].[OneToMany_Required_Inverse2Id] IS NOT NULL THEN [s].[Id0] END = [l4].[OneToMany_Required_Inverse3Id] -WHERE [l].[Name] IN ( - SELECT [v].[value] - FROM OPENJSON(@validIds) WITH ([value] nvarchar(max) '$') AS [v] -) +WHERE [l].[Name] IN (@validIds1, @validIds2) ORDER BY [l].[Id], [s].[Id], [s].[Id0] """); } @@ -3012,25 +3010,20 @@ public override async Task Collection_projection_over_GroupBy_over_parameter(boo AssertSql( """ -@validIds='["L1 01","L1 02"]' (Size = 4000) +@validIds1='L1 01' (Size = 4000) +@validIds2='L1 02' (Size = 4000) SELECT [l1].[Date], [l2].[Id] FROM ( SELECT [l].[Date] FROM [Level1] AS [l] - WHERE [l].[Name] IN ( - SELECT [v].[value] - FROM OPENJSON(@validIds) WITH ([value] nvarchar(max) '$') AS [v] - ) + WHERE [l].[Name] IN (@validIds1, @validIds2) GROUP BY [l].[Date] ) AS [l1] LEFT JOIN ( SELECT [l0].[Id], [l0].[Date] FROM [Level1] AS [l0] - WHERE [l0].[Name] IN ( - SELECT [v0].[value] - FROM OPENJSON(@validIds) WITH ([value] nvarchar(max) '$') AS [v0] - ) + WHERE [l0].[Name] IN (@validIds1, @validIds2) ) AS [l2] ON [l1].[Date] = [l2].[Date] ORDER BY [l1].[Date] """); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsCollectionsSplitQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsCollectionsSplitQuerySqlServerTest.cs index 944f4f73611..3289427b405 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsCollectionsSplitQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsCollectionsSplitQuerySqlServerTest.cs @@ -3189,7 +3189,8 @@ public override async Task LeftJoin_with_Any_on_outer_source_and_projecting_coll AssertSql( """ -@validIds='["L1 01","L1 02"]' (Size = 4000) +@validIds1='L1 01' (Size = 4000) +@validIds2='L1 02' (Size = 4000) SELECT CASE WHEN [l0].[Id] IS NULL THEN 0 @@ -3197,24 +3198,19 @@ ELSE [l0].[Id] END, [l].[Id], [l0].[Id] FROM [LevelOne] AS [l] LEFT JOIN [LevelTwo] AS [l0] ON [l].[Id] = [l0].[Level1_Required_Id] -WHERE [l].[Name] IN ( - SELECT [v].[value] - FROM OPENJSON(@validIds) WITH ([value] nvarchar(max) '$') AS [v] -) +WHERE [l].[Name] IN (@validIds1, @validIds2) ORDER BY [l].[Id], [l0].[Id] """, // """ -@validIds='["L1 01","L1 02"]' (Size = 4000) +@validIds3='L1 01' (Size = 4000) +@validIds4='L1 02' (Size = 4000) SELECT [l1].[Id], [l1].[Level2_Optional_Id], [l1].[Level2_Required_Id], [l1].[Name], [l1].[OneToMany_Optional_Inverse3Id], [l1].[OneToMany_Optional_Self_Inverse3Id], [l1].[OneToMany_Required_Inverse3Id], [l1].[OneToMany_Required_Self_Inverse3Id], [l1].[OneToOne_Optional_PK_Inverse3Id], [l1].[OneToOne_Optional_Self3Id], [l].[Id], [l0].[Id] FROM [LevelOne] AS [l] LEFT JOIN [LevelTwo] AS [l0] ON [l].[Id] = [l0].[Level1_Required_Id] INNER JOIN [LevelThree] AS [l1] ON [l0].[Id] = [l1].[OneToMany_Required_Inverse3Id] -WHERE [l].[Name] IN ( - SELECT [v].[value] - FROM OPENJSON(@validIds) WITH ([value] nvarchar(max) '$') AS [v] -) +WHERE [l].[Name] IN (@validIds3, @validIds4) ORDER BY [l].[Id], [l0].[Id] """); } @@ -3747,38 +3743,31 @@ public override async Task Collection_projection_over_GroupBy_over_parameter(boo AssertSql( """ -@validIds='["L1 01","L1 02"]' (Size = 4000) +@validIds1='L1 01' (Size = 4000) +@validIds2='L1 02' (Size = 4000) SELECT [l].[Date] FROM [LevelOne] AS [l] -WHERE [l].[Name] IN ( - SELECT [v].[value] - FROM OPENJSON(@validIds) WITH ([value] nvarchar(max) '$') AS [v] -) +WHERE [l].[Name] IN (@validIds1, @validIds2) GROUP BY [l].[Date] ORDER BY [l].[Date] """, // """ -@validIds='["L1 01","L1 02"]' (Size = 4000) +@validIds3='L1 01' (Size = 4000) +@validIds4='L1 02' (Size = 4000) SELECT [l5].[Id], [l4].[Date] FROM ( SELECT [l].[Date] FROM [LevelOne] AS [l] - WHERE [l].[Name] IN ( - SELECT [v].[value] - FROM OPENJSON(@validIds) WITH ([value] nvarchar(max) '$') AS [v] - ) + WHERE [l].[Name] IN (@validIds3, @validIds4) GROUP BY [l].[Date] ) AS [l4] INNER JOIN ( SELECT [l3].[Id], [l3].[Date] FROM [LevelOne] AS [l3] - WHERE [l3].[Name] IN ( - SELECT [v3].[value] - FROM OPENJSON(@validIds) WITH ([value] nvarchar(max) '$') AS [v3] - ) + WHERE [l3].[Name] IN (@validIds3, @validIds4) ) AS [l5] ON [l4].[Date] = [l5].[Date] ORDER BY [l4].[Date] """); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsQuerySqlServer160Test.cs b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsQuerySqlServer160Test.cs index 23a69d6f6c0..5d35101d5d7 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsQuerySqlServer160Test.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsQuerySqlServer160Test.cs @@ -3141,15 +3141,13 @@ public override async Task Accessing_optional_property_inside_result_operator_su AssertSql( """ -@names='["Name1","Name2"]' (Size = 4000) +@names1='Name1' (Size = 4000) +@names2='Name2' (Size = 4000) SELECT [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_Inverse1Id], [l].[OneToMany_Required_Self_Inverse1Id], [l].[OneToOne_Optional_Self1Id] FROM [LevelOne] AS [l] LEFT JOIN [LevelTwo] AS [l0] ON [l].[Id] = [l0].[Level1_Optional_Id] -WHERE [l0].[Name] NOT IN ( - SELECT [n].[value] - FROM OPENJSON(@names) WITH ([value] nvarchar(max) '$') AS [n] -) OR [l0].[Name] IS NULL +WHERE [l0].[Name] NOT IN (@names1, @names2) OR [l0].[Name] IS NULL """); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsQuerySqlServerTest.cs index 12eff52e794..39ef30c31cc 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsQuerySqlServerTest.cs @@ -3141,15 +3141,13 @@ public override async Task Accessing_optional_property_inside_result_operator_su AssertSql( """ -@names='["Name1","Name2"]' (Size = 4000) +@names1='Name1' (Size = 4000) +@names2='Name2' (Size = 4000) SELECT [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_Inverse1Id], [l].[OneToMany_Required_Self_Inverse1Id], [l].[OneToOne_Optional_Self1Id] FROM [LevelOne] AS [l] LEFT JOIN [LevelTwo] AS [l0] ON [l].[Id] = [l0].[Level1_Optional_Id] -WHERE [l0].[Name] NOT IN ( - SELECT [n].[value] - FROM OPENJSON(@names) WITH ([value] nvarchar(max) '$') AS [n] -) OR [l0].[Name] IS NULL +WHERE [l0].[Name] NOT IN (@names1, @names2) OR [l0].[Name] IS NULL """); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsSharedTypeQuerySqlServer160Test.cs b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsSharedTypeQuerySqlServer160Test.cs index e1e3b9b920f..fc9dfb99b1b 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsSharedTypeQuerySqlServer160Test.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsSharedTypeQuerySqlServer160Test.cs @@ -5564,7 +5564,8 @@ public override async Task Accessing_optional_property_inside_result_operator_su AssertSql( """ -@names='["Name1","Name2"]' (Size = 4000) +@names1='Name1' (Size = 4000) +@names2='Name2' (Size = 4000) SELECT [l].[Id], [l].[Date], [l].[Name] FROM [Level1] AS [l] @@ -5573,10 +5574,7 @@ LEFT JOIN ( FROM [Level1] AS [l0] WHERE [l0].[OneToOne_Required_PK_Date] IS NOT NULL AND [l0].[Level1_Required_Id] IS NOT NULL AND [l0].[OneToMany_Required_Inverse2Id] IS NOT NULL ) AS [l1] ON [l].[Id] = [l1].[Level1_Optional_Id] -WHERE [l1].[Level2_Name] NOT IN ( - SELECT [n].[value] - FROM OPENJSON(@names) WITH ([value] nvarchar(max) '$') AS [n] -) OR [l1].[Level2_Name] IS NULL +WHERE [l1].[Level2_Name] NOT IN (@names1, @names2) OR [l1].[Level2_Name] IS NULL """); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsSharedTypeQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsSharedTypeQuerySqlServerTest.cs index c0d96ba5534..0dc95baee7c 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsSharedTypeQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsSharedTypeQuerySqlServerTest.cs @@ -5566,7 +5566,8 @@ public override async Task Accessing_optional_property_inside_result_operator_su AssertSql( """ -@names='["Name1","Name2"]' (Size = 4000) +@names1='Name1' (Size = 4000) +@names2='Name2' (Size = 4000) SELECT [l].[Id], [l].[Date], [l].[Name] FROM [Level1] AS [l] @@ -5575,10 +5576,7 @@ LEFT JOIN ( FROM [Level1] AS [l0] WHERE [l0].[OneToOne_Required_PK_Date] IS NOT NULL AND [l0].[Level1_Required_Id] IS NOT NULL AND [l0].[OneToMany_Required_Inverse2Id] IS NOT NULL ) AS [l1] ON [l].[Id] = [l1].[Level1_Optional_Id] -WHERE [l1].[Level2_Name] NOT IN ( - SELECT [n].[value] - FROM OPENJSON(@names) WITH ([value] nvarchar(max) '$') AS [n] -) OR [l1].[Level2_Name] IS NULL +WHERE [l1].[Level2_Name] NOT IN (@names1, @names2) OR [l1].[Level2_Name] IS NULL """); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs index 23d22236769..ebecdfadd26 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs @@ -208,15 +208,17 @@ FROM [Tags] AS [t] """, // """ -@tags='["34c8d86e-a4ac-4be5-827f-584dda348a07","df36f493-463f-4123-83f9-6b135deeb7ba","a8ad98f9-e023-4e2a-9a70-c2728455bd34","70534e05-782c-4052-8720-c2c54481ce5f","a7be028a-0cf2-448f-ab55-ce8bc5d8cf69","b39a6fba-9026-4d69-828e-fd7068673e57"]' (Size = 4000) +@tags1='34c8d86e-a4ac-4be5-827f-584dda348a07' +@tags2='df36f493-463f-4123-83f9-6b135deeb7ba' +@tags3='a8ad98f9-e023-4e2a-9a70-c2728455bd34' +@tags4='70534e05-782c-4052-8720-c2c54481ce5f' +@tags5='a7be028a-0cf2-448f-ab55-ce8bc5d8cf69' +@tags6='b39a6fba-9026-4d69-828e-fd7068673e57' SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank], [t].[Id], [t].[GearNickName], [t].[GearSquadId], [t].[IssueDate], [t].[Note] FROM [Gears] AS [g] LEFT JOIN [Tags] AS [t] ON [g].[Nickname] = [t].[GearNickName] AND [g].[SquadId] = [t].[GearSquadId] -WHERE [t].[Id] IS NOT NULL AND [t].[Id] IN ( - SELECT [t0].[value] - FROM OPENJSON(@tags) WITH ([value] uniqueidentifier '$') AS [t0] -) +WHERE [t].[Id] IS NOT NULL AND [t].[Id] IN (@tags1, @tags2, @tags3, @tags4, @tags5, @tags6) """); } @@ -231,16 +233,18 @@ FROM [Tags] AS [t] """, // """ -@tags='["34c8d86e-a4ac-4be5-827f-584dda348a07","df36f493-463f-4123-83f9-6b135deeb7ba","a8ad98f9-e023-4e2a-9a70-c2728455bd34","70534e05-782c-4052-8720-c2c54481ce5f","a7be028a-0cf2-448f-ab55-ce8bc5d8cf69","b39a6fba-9026-4d69-828e-fd7068673e57"]' (Size = 4000) +@tags1='34c8d86e-a4ac-4be5-827f-584dda348a07' +@tags2='df36f493-463f-4123-83f9-6b135deeb7ba' +@tags3='a8ad98f9-e023-4e2a-9a70-c2728455bd34' +@tags4='70534e05-782c-4052-8720-c2c54481ce5f' +@tags5='a7be028a-0cf2-448f-ab55-ce8bc5d8cf69' +@tags6='b39a6fba-9026-4d69-828e-fd7068673e57' SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank], [t].[Id], [t].[GearNickName], [t].[GearSquadId], [t].[IssueDate], [t].[Note] FROM [Gears] AS [g] INNER JOIN [Cities] AS [c] ON [g].[CityOfBirthName] = [c].[Name] LEFT JOIN [Tags] AS [t] ON [g].[Nickname] = [t].[GearNickName] AND [g].[SquadId] = [t].[GearSquadId] -WHERE [c].[Location] IS NOT NULL AND [t].[Id] IN ( - SELECT [t0].[value] - FROM OPENJSON(@tags) WITH ([value] uniqueidentifier '$') AS [t0] -) +WHERE [c].[Location] IS NOT NULL AND [t].[Id] IN (@tags1, @tags2, @tags3, @tags4, @tags5, @tags6) """); } @@ -255,15 +259,17 @@ FROM [Tags] AS [t] """, // """ -@tags='["34c8d86e-a4ac-4be5-827f-584dda348a07","df36f493-463f-4123-83f9-6b135deeb7ba","a8ad98f9-e023-4e2a-9a70-c2728455bd34","70534e05-782c-4052-8720-c2c54481ce5f","a7be028a-0cf2-448f-ab55-ce8bc5d8cf69","b39a6fba-9026-4d69-828e-fd7068673e57"]' (Size = 4000) +@tags1='34c8d86e-a4ac-4be5-827f-584dda348a07' +@tags2='df36f493-463f-4123-83f9-6b135deeb7ba' +@tags3='a8ad98f9-e023-4e2a-9a70-c2728455bd34' +@tags4='70534e05-782c-4052-8720-c2c54481ce5f' +@tags5='a7be028a-0cf2-448f-ab55-ce8bc5d8cf69' +@tags6='b39a6fba-9026-4d69-828e-fd7068673e57' SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank] FROM [Gears] AS [g] LEFT JOIN [Tags] AS [t] ON [g].[Nickname] = [t].[GearNickName] AND [g].[SquadId] = [t].[GearSquadId] -WHERE [t].[Id] IS NOT NULL AND [t].[Id] IN ( - SELECT [t0].[value] - FROM OPENJSON(@tags) WITH ([value] uniqueidentifier '$') AS [t0] -) +WHERE [t].[Id] IS NOT NULL AND [t].[Id] IN (@tags1, @tags2, @tags3, @tags4, @tags5, @tags6) """); } @@ -1759,14 +1765,12 @@ public override async Task Non_unicode_string_literals_in_contains_is_used_for_n AssertSql( """ -@cities='["Unknown","Jacinto\u0027s location","Ephyra\u0027s location"]' (Size = 4000) +@cities1='Unknown' (Size = 100) (DbType = AnsiString) +@cities2='Jacinto's location' (Size = 100) (DbType = AnsiString), @cities3='Ephyra's location' (Size = 100) (DbType = AnsiString) SELECT [c].[Name], [c].[Location], [c].[Nation] FROM [Cities] AS [c] -WHERE [c].[Location] IN ( - SELECT [c0].[value] - FROM OPENJSON(@cities) WITH ([value] varchar(100) '$') AS [c0] -) +WHERE [c].[Location] IN (@cities1, @cities2, @cities3) """); } @@ -2555,14 +2559,13 @@ public override async Task Contains_with_local_nullable_guid_list_closure(bool a AssertSql( """ -@ids='["df36f493-463f-4123-83f9-6b135deeb7ba","23cbcf9b-ce14-45cf-aafa-2c2667ebfdd3","ab1b82d7-88db-42bd-a132-7eef9aa68af4"]' (Size = 4000) +@ids1='df36f493-463f-4123-83f9-6b135deeb7ba' +@ids2='23cbcf9b-ce14-45cf-aafa-2c2667ebfdd3' +@ids3='ab1b82d7-88db-42bd-a132-7eef9aa68af4' SELECT [t].[Id], [t].[GearNickName], [t].[GearSquadId], [t].[IssueDate], [t].[Note] FROM [Tags] AS [t] -WHERE [t].[Id] IN ( - SELECT [i].[value] - FROM OPENJSON(@ids) WITH ([value] uniqueidentifier '$') AS [i] -) +WHERE [t].[Id] IN (@ids1, @ids2, @ids3) """); } @@ -3078,15 +3081,12 @@ public override async Task Contains_on_nullable_array_produces_correct_sql(bool AssertSql( """ -@cities_without_nulls='["Ephyra"]' (Size = 4000) +@cities1='Ephyra' (Size = 450) SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank] FROM [Gears] AS [g] LEFT JOIN [Cities] AS [c] ON [g].[AssignedCityName] = [c].[Name] -WHERE [g].[SquadId] < 2 AND ([c].[Name] IN ( - SELECT [c0].[value] - FROM OPENJSON(@cities_without_nulls) AS [c0] -) OR [c].[Name] IS NULL) +WHERE [g].[SquadId] < 2 AND ([c].[Name] IS NULL OR [c].[Name] = @cities1) """); } @@ -5450,18 +5450,10 @@ public override async Task Correlated_collection_with_complex_order_by_funcletiz AssertSql( """ -@nicknames='[]' (Size = 4000) - SELECT [g].[Nickname], [g].[SquadId], [w].[Name], [w].[Id] FROM [Gears] AS [g] LEFT JOIN [Weapons] AS [w] ON [g].[FullName] = [w].[OwnerFullName] -ORDER BY CASE - WHEN [g].[Nickname] IN ( - SELECT [n].[value] - FROM OPENJSON(@nicknames) WITH ([value] nvarchar(450) '$') AS [n] - ) THEN CAST(1 AS bit) - ELSE CAST(0 AS bit) -END DESC, [g].[Nickname], [g].[SquadId] +ORDER BY [g].[Nickname], [g].[SquadId] """); } @@ -6142,14 +6134,11 @@ public override async Task DateTimeOffset_Contains_Less_than_Greater_than(bool a """ @start='1902-01-01T10:00:00.1234567+01:30' @end='1902-01-03T10:00:00.1234567+01:30' -@dates='["1902-01-02T10:00:00.1234567+01:30"]' (Size = 4000) +@dates1='1902-01-02T10:00:00.1234567+01:30' SELECT [m].[Id], [m].[BriefingDocument], [m].[BriefingDocumentFileExtension], [m].[CodeName], [m].[Date], [m].[Difficulty], [m].[Duration], [m].[Rating], [m].[Time], [m].[Timeline] FROM [Missions] AS [m] -WHERE @start <= CAST(CONVERT(date, [m].[Timeline]) AS datetimeoffset) AND [m].[Timeline] < @end AND [m].[Timeline] IN ( - SELECT [d].[value] - FROM OPENJSON(@dates) WITH ([value] datetimeoffset '$') AS [d] -) +WHERE @start <= CAST(CONVERT(date, [m].[Timeline]) AS datetimeoffset) AND [m].[Timeline] < @end AND [m].[Timeline] = @dates1 """); } @@ -6858,17 +6847,8 @@ public override async Task OrderBy_Contains_empty_list(bool async) AssertSql( """ -@ids='[]' (Size = 4000) - SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank] FROM [Gears] AS [g] -ORDER BY CASE - WHEN [g].[SquadId] IN ( - SELECT [i].[value] - FROM OPENJSON(@ids) WITH ([value] int '$') AS [i] - ) THEN CAST(1 AS bit) - ELSE CAST(0 AS bit) -END """); } @@ -7419,15 +7399,12 @@ public override async Task Enum_array_contains(bool async) AssertSql( """ -@types_without_nulls='[1]' (Size = 4000) +@types2='1' SELECT [w].[Id], [w].[AmmunitionType], [w].[IsAutomatic], [w].[Name], [w].[OwnerFullName], [w].[SynergyWithId] FROM [Weapons] AS [w] LEFT JOIN [Weapons] AS [w0] ON [w].[SynergyWithId] = [w0].[Id] -WHERE [w0].[Id] IS NOT NULL AND ([w0].[AmmunitionType] IN ( - SELECT [t].[value] - FROM OPENJSON(@types_without_nulls) AS [t] -) OR [w0].[AmmunitionType] IS NULL) +WHERE [w0].[Id] IS NOT NULL AND ([w0].[AmmunitionType] IS NULL OR [w0].[AmmunitionType] = @types2) """); } @@ -8444,14 +8421,12 @@ public override async Task Where_bool_column_and_Contains(bool async) AssertSql( """ -@values='[false,true]' (Size = 4000) +@values1='False' +@values2='True' SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank] FROM [Gears] AS [g] -WHERE [g].[HasSoulPatch] = CAST(1 AS bit) AND [g].[HasSoulPatch] IN ( - SELECT [v].[value] - FROM OPENJSON(@values) WITH ([value] bit '$') AS [v] -) +WHERE [g].[HasSoulPatch] = CAST(1 AS bit) AND [g].[HasSoulPatch] IN (@values1, @values2) """); } @@ -8461,14 +8436,12 @@ public override async Task Where_bool_column_or_Contains(bool async) AssertSql( """ -@values='[false,true]' (Size = 4000) +@values1='False' +@values2='True' SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank] FROM [Gears] AS [g] -WHERE [g].[HasSoulPatch] = CAST(1 AS bit) AND [g].[HasSoulPatch] IN ( - SELECT [v].[value] - FROM OPENJSON(@values) WITH ([value] bit '$') AS [v] -) +WHERE [g].[HasSoulPatch] = CAST(1 AS bit) AND [g].[HasSoulPatch] IN (@values1, @values2) """); } @@ -9156,7 +9129,8 @@ public override async Task Nav_expansion_inside_Contains_argument(bool async) AssertSql( """ -@numbers='[1,-1]' (Size = 4000) +@numbers1='1' +@numbers2='-1' SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank] FROM [Gears] AS [g] @@ -9166,10 +9140,7 @@ SELECT 1 FROM [Weapons] AS [w] WHERE [g].[FullName] = [w].[OwnerFullName]) THEN 1 ELSE 0 -END IN ( - SELECT [n].[value] - FROM OPENJSON(@numbers) WITH ([value] int '$') AS [n] -) +END IN (@numbers1, @numbers2) """); } @@ -9179,7 +9150,7 @@ public override async Task Nav_expansion_with_member_pushdown_inside_Contains_ar AssertSql( """ -@weapons='["Marcus\u0027 Lancer","Dom\u0027s Gnasher"]' (Size = 4000) +@weapons1='Marcus' Lancer' (Size = 4000), @weapons2='Dom's Gnasher' (Size = 4000) SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank] FROM [Gears] AS [g] @@ -9187,10 +9158,7 @@ FROM [Gears] AS [g] SELECT TOP(1) [w0].[Name] FROM [Weapons] AS [w0] WHERE [g].[FullName] = [w0].[OwnerFullName] - ORDER BY [w0].[Id]) IN ( - SELECT [w].[value] - FROM OPENJSON(@weapons) WITH ([value] nvarchar(max) '$') AS [w] -) + ORDER BY [w0].[Id]) IN (@weapons1, @weapons2) """); } @@ -9200,7 +9168,9 @@ public override async Task Subquery_inside_Take_argument(bool async) AssertSql( """ -@numbers='[0,1,2]' (Size = 4000) +@numbers1='0' +@numbers2='1' +@numbers3='2' SELECT [g].[Nickname], [g].[SquadId], [w1].[Id], [w1].[AmmunitionType], [w1].[IsAutomatic], [w1].[Name], [w1].[OwnerFullName], [w1].[SynergyWithId] FROM [Gears] AS [g] @@ -9211,9 +9181,9 @@ LEFT JOIN ( FROM [Weapons] AS [w] ) AS [w0] WHERE [w0].[row] <= ISNULL(( - SELECT [n].[value] - FROM OPENJSON(@numbers) WITH ([value] int '$') AS [n] - ORDER BY [n].[value] + SELECT [n].[Value] + FROM (VALUES (1, @numbers1), (2, @numbers2), (3, @numbers3)) AS [n]([_ord], [Value]) + ORDER BY [n].[Value] OFFSET 1 ROWS FETCH NEXT 1 ROWS ONLY), 0) ) AS [w1] ON [g].[FullName] = [w1].[OwnerFullName] ORDER BY [g].[Nickname], [g].[SquadId], [w1].[OwnerFullName], [w1].[Id] @@ -9376,41 +9346,31 @@ public override async Task Nested_contains_with_enum(bool async) AssertSql( """ -@ranks='[1]' (Size = 4000) +@ranks1='1' @key='5f221fb9-66f4-442a-92c9-d97ed5989cc7' -@keys='["0a47bcb7-a1cb-4345-8944-c58f82d6aac7","5f221fb9-66f4-442a-92c9-d97ed5989cc7"]' (Size = 4000) +@keys1='0a47bcb7-a1cb-4345-8944-c58f82d6aac7' +@keys2='5f221fb9-66f4-442a-92c9-d97ed5989cc7' SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank] FROM [Gears] AS [g] WHERE CASE - WHEN [g].[Rank] IN ( - SELECT [r].[value] - FROM OPENJSON(@ranks) WITH ([value] int '$') AS [r] - ) THEN @key + WHEN [g].[Rank] = @ranks1 THEN @key ELSE @key -END IN ( - SELECT [k].[value] - FROM OPENJSON(@keys) WITH ([value] uniqueidentifier '$') AS [k] -) +END IN (@keys1, @keys2) """, // """ -@ammoTypes='[1]' (Size = 4000) +@ammoTypes1='1' @key='5f221fb9-66f4-442a-92c9-d97ed5989cc7' -@keys='["0a47bcb7-a1cb-4345-8944-c58f82d6aac7","5f221fb9-66f4-442a-92c9-d97ed5989cc7"]' (Size = 4000) +@keys1='0a47bcb7-a1cb-4345-8944-c58f82d6aac7' +@keys2='5f221fb9-66f4-442a-92c9-d97ed5989cc7' SELECT [w].[Id], [w].[AmmunitionType], [w].[IsAutomatic], [w].[Name], [w].[OwnerFullName], [w].[SynergyWithId] FROM [Weapons] AS [w] WHERE CASE - WHEN [w].[AmmunitionType] IN ( - SELECT [a].[value] - FROM OPENJSON(@ammoTypes) WITH ([value] int '$') AS [a] - ) THEN @key + WHEN [w].[AmmunitionType] = @ammoTypes1 THEN @key ELSE @key -END IN ( - SELECT [k].[value] - FROM OPENJSON(@keys) WITH ([value] uniqueidentifier '$') AS [k] -) +END IN (@keys1, @keys2) """); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NonSharedPrimitiveCollectionsQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NonSharedPrimitiveCollectionsQuerySqlServerTest.cs index 9429b192485..3923e6cb3f4 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/NonSharedPrimitiveCollectionsQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/NonSharedPrimitiveCollectionsQuerySqlServerTest.cs @@ -24,6 +24,13 @@ protected override DbContextOptionsBuilder SetTranslateParameterizedCollectionsT return optionsBuilder; } + protected override DbContextOptionsBuilder SetTranslateParameterizedCollectionsToExpandedParameters(DbContextOptionsBuilder optionsBuilder) + { + new SqlServerDbContextOptionsBuilder(optionsBuilder).TranslateParameterizedCollectionsToExpandedParameters(); + + return optionsBuilder; + } + #region Support for specific element types public override async Task Array_of_string() @@ -807,9 +814,9 @@ SELECT COUNT(*) """); } - public override async Task Parameter_collection_of_ints_Contains_int_with_default_constants() + public override async Task Parameter_collection_Contains_with_default_constants() { - await base.Parameter_collection_of_ints_Contains_int_with_default_constants(); + await base.Parameter_collection_Contains_with_default_constants(); AssertSql( """ @@ -836,9 +843,9 @@ FROM OPENJSON(@ids) WITH ([value] int '$') AS [i] """); } - public override async Task Parameter_collection_of_ints_Contains_int_with_default_constants_EF_Parameter() + public override async Task Parameter_collection_Contains_with_default_constants_EF_Parameter() { - await base.Parameter_collection_of_ints_Contains_int_with_default_constants_EF_Parameter(); + await base.Parameter_collection_Contains_with_default_constants_EF_Parameter(); AssertSql( """ @@ -870,9 +877,9 @@ FROM OPENJSON(@ids) WITH ([value] int '$') AS [i] """); } - public override async Task Parameter_collection_of_ints_Contains_int_with_default_parameters() + public override async Task Parameter_collection_Contains_with_default_parameters() { - await base.Parameter_collection_of_ints_Contains_int_with_default_parameters(); + await base.Parameter_collection_Contains_with_default_parameters(); AssertSql( """ @@ -902,9 +909,9 @@ SELECT COUNT(*) """); } - public override async Task Parameter_collection_of_ints_Contains_int_with_default_parameters_EF_Constant() + public override async Task Parameter_collection_Contains_with_default_parameters_EF_Constant() { - await base.Parameter_collection_of_ints_Contains_int_with_default_parameters_EF_Constant(); + await base.Parameter_collection_Contains_with_default_parameters_EF_Constant(); AssertSql( """ @@ -914,6 +921,39 @@ WHERE [t].[Id] IN (2, 999) """); } + public override async Task Parameter_collection_Count_with_column_predicate_with_default_expanded_parameters() + { + await base.Parameter_collection_Count_with_column_predicate_with_default_expanded_parameters(); + + AssertSql( + """ +@ids1='2' +@ids2='999' + +SELECT [t].[Id] +FROM [TestEntity] AS [t] +WHERE ( + SELECT COUNT(*) + FROM (VALUES (@ids1), (@ids2)) AS [i]([Value]) + WHERE [i].[Value] > [t].[Id]) = 1 +"""); + } + + public override async Task Parameter_collection_Contains_with_default_expanded_parameters() + { + await base.Parameter_collection_Contains_with_default_expanded_parameters(); + + AssertSql( + """ +@ints1='2' +@ints2='999' + +SELECT [t].[Id] +FROM [TestEntity] AS [t] +WHERE [t].[Id] IN (@ints1, @ints2) +"""); + } + [ConditionalFact] public virtual async Task Same_parameter_with_different_type_mappings() { @@ -938,18 +978,14 @@ public virtual async Task Same_parameter_with_different_type_mappings() AssertSql( """ -@dateTimes='["2020-01-01T12:30:00","2020-01-02T12:30:00"]' (Size = 4000) -@dateTimes0='["2020-01-01T12:30:00","2020-01-02T12:30:00"]' (Size = 4000) +@dateTimes1='2020-01-01T12:30:00.0000000' (DbType = DateTime) +@dateTimes2='2020-01-02T12:30:00.0000000' (DbType = DateTime) +@dateTimes3='2020-01-01T12:30:00.0000000' +@dateTimes4='2020-01-02T12:30:00.0000000' SELECT [t].[Id], [t].[DateTime], [t].[DateTime2], [t].[Ints] FROM [TestEntity] AS [t] -WHERE [t].[DateTime] IN ( - SELECT [d].[value] - FROM OPENJSON(@dateTimes) WITH ([value] datetime '$') AS [d] -) AND [t].[DateTime2] IN ( - SELECT [d0].[value] - FROM OPENJSON(@dateTimes0) WITH ([value] datetime2 '$') AS [d0] -) +WHERE [t].[DateTime] IN (@dateTimes1, @dateTimes2) AND [t].[DateTime2] IN (@dateTimes3, @dateTimes4) """); } @@ -969,14 +1005,16 @@ public virtual async Task Same_collection_with_default_type_mapping_and_uninferr AssertSql( """ -@dateTimes='["2020-01-01T12:30:00","2020-01-02T12:30:00",null]' (Size = 4000) +@dateTimes1='2020-01-01T12:30:00.0000000' +@dateTimes2='2020-01-02T12:30:00.0000000' +@dateTimes3=NULL (DbType = DateTime2) SELECT [t].[Id], [t].[DateTime], [t].[Ints] FROM [TestEntity] AS [t] WHERE EXISTS ( SELECT 1 - FROM OPENJSON(@dateTimes) WITH ([value] datetime2 '$') AS [d] - WHERE [d].[value] = [t].[DateTime] AND [d].[value] IS NOT NULL) + FROM (VALUES (@dateTimes1), (@dateTimes2), (@dateTimes3)) AS [d]([Value]) + WHERE [d].[Value] = [t].[DateTime] AND [d].[Value] IS NOT NULL) """); } @@ -996,7 +1034,7 @@ public virtual async Task Same_collection_with_non_default_type_mapping_and_unin .Where( m => dateTimes.Any(d => d == EF.Property(m, "DateTime") && d != null)) .ToArrayAsync()); - Assert.Equal(RelationalStrings.ConflictingTypeMappingsInferredForColumn("value"), exception.Message); + Assert.Equal(RelationalStrings.ConflictingTypeMappingsInferredForColumn("Value"), exception.Message); } [ConditionalFact] @@ -1020,7 +1058,7 @@ public virtual async Task Same_collection_with_conflicting_type_mappings_not_sup m => dateTimes .Any(d => d == EF.Property(m, "DateTime") && d == EF.Property(m, "DateTime2"))) .ToArrayAsync()); - Assert.Equal(RelationalStrings.ConflictingTypeMappingsInferredForColumn("value"), exception.Message); + Assert.Equal(RelationalStrings.ConflictingTypeMappingsInferredForColumn("Value"), exception.Message); } [ConditionalFact] diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindAggregateOperatorsQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindAggregateOperatorsQuerySqlServerTest.cs index f4b9823bd0c..85a701c406e 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindAggregateOperatorsQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindAggregateOperatorsQuerySqlServerTest.cs @@ -1629,25 +1629,20 @@ public override async Task Contains_with_local_array_closure(bool async) AssertSql( """ -@ids='["ABCDE","ALFKI"]' (Size = 4000) +@ids1='ABCDE' (Size = 5) (DbType = StringFixedLength) +@ids2='ALFKI' (Size = 5) (DbType = StringFixedLength) SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[CustomerID] IN ( - SELECT [i].[value] - FROM OPENJSON(@ids) WITH ([value] nchar(5) '$') AS [i] -) +WHERE [c].[CustomerID] IN (@ids1, @ids2) """, // """ -@ids='["ABCDE"]' (Size = 4000) +@ids1='ABCDE' (Size = 5) (DbType = StringFixedLength) SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[CustomerID] IN ( - SELECT [i].[value] - FROM OPENJSON(@ids) WITH ([value] nchar(5) '$') AS [i] -) +WHERE [c].[CustomerID] = @ids1 """); } @@ -1657,31 +1652,26 @@ public override async Task Contains_with_subquery_and_local_array_closure(bool a AssertSql( """ -@ids='["London","Buenos Aires"]' (Size = 4000) +@ids1='London' (Size = 15) +@ids2='Buenos Aires' (Size = 15) SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] WHERE EXISTS ( SELECT 1 FROM [Customers] AS [c0] - WHERE [c0].[City] IN ( - SELECT [i].[value] - FROM OPENJSON(@ids) WITH ([value] nvarchar(15) '$') AS [i] - ) AND [c0].[CustomerID] = [c].[CustomerID]) + WHERE [c0].[City] IN (@ids1, @ids2) AND [c0].[CustomerID] = [c].[CustomerID]) """, // """ -@ids='["London"]' (Size = 4000) +@ids1='London' (Size = 15) SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] WHERE EXISTS ( SELECT 1 FROM [Customers] AS [c0] - WHERE [c0].[City] IN ( - SELECT [i].[value] - FROM OPENJSON(@ids) WITH ([value] nvarchar(15) '$') AS [i] - ) AND [c0].[CustomerID] = [c].[CustomerID]) + WHERE [c0].[City] = @ids1 AND [c0].[CustomerID] = [c].[CustomerID]) """); } @@ -1691,25 +1681,20 @@ public override async Task Contains_with_local_uint_array_closure(bool async) AssertSql( """ -@ids='[0,1]' (Size = 4000) +@ids1='0' +@ids2='1' SELECT [e].[EmployeeID], [e].[City], [e].[Country], [e].[FirstName], [e].[ReportsTo], [e].[Title] FROM [Employees] AS [e] -WHERE [e].[EmployeeID] IN ( - SELECT [i].[value] - FROM OPENJSON(@ids) WITH ([value] int '$') AS [i] -) +WHERE [e].[EmployeeID] IN (@ids1, @ids2) """, // """ -@ids='[0]' (Size = 4000) +@ids1='0' SELECT [e].[EmployeeID], [e].[City], [e].[Country], [e].[FirstName], [e].[ReportsTo], [e].[Title] FROM [Employees] AS [e] -WHERE [e].[EmployeeID] IN ( - SELECT [i].[value] - FROM OPENJSON(@ids) WITH ([value] int '$') AS [i] -) +WHERE [e].[EmployeeID] = @ids1 """); } @@ -1719,25 +1704,20 @@ public override async Task Contains_with_local_nullable_uint_array_closure(bool AssertSql( """ -@ids='[0,1]' (Size = 4000) +@ids1='0' +@ids2='1' SELECT [e].[EmployeeID], [e].[City], [e].[Country], [e].[FirstName], [e].[ReportsTo], [e].[Title] FROM [Employees] AS [e] -WHERE [e].[EmployeeID] IN ( - SELECT [i].[value] - FROM OPENJSON(@ids) WITH ([value] int '$') AS [i] -) +WHERE [e].[EmployeeID] IN (@ids1, @ids2) """, // """ -@ids='[0]' (Size = 4000) +@ids1='0' SELECT [e].[EmployeeID], [e].[City], [e].[Country], [e].[FirstName], [e].[ReportsTo], [e].[Title] FROM [Employees] AS [e] -WHERE [e].[EmployeeID] IN ( - SELECT [i].[value] - FROM OPENJSON(@ids) WITH ([value] int '$') AS [i] -) +WHERE [e].[EmployeeID] = @ids1 """); } @@ -1759,14 +1739,12 @@ public override async Task Contains_with_local_list_closure(bool async) AssertSql( """ -@ids='["ABCDE","ALFKI"]' (Size = 4000) +@ids1='ABCDE' (Size = 5) (DbType = StringFixedLength) +@ids2='ALFKI' (Size = 5) (DbType = StringFixedLength) SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[CustomerID] IN ( - SELECT [i].[value] - FROM OPENJSON(@ids) WITH ([value] nchar(5) '$') AS [i] -) +WHERE [c].[CustomerID] IN (@ids1, @ids2) """); } @@ -1776,14 +1754,12 @@ public override async Task Contains_with_local_object_list_closure(bool async) AssertSql( """ -@ids='["ABCDE","ALFKI"]' (Size = 4000) +@ids1='ABCDE' (Size = 5) (DbType = StringFixedLength) +@ids2='ALFKI' (Size = 5) (DbType = StringFixedLength) SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[CustomerID] IN ( - SELECT [i].[value] - FROM OPENJSON(@ids) WITH ([value] nchar(5) '$') AS [i] -) +WHERE [c].[CustomerID] IN (@ids1, @ids2) """); } @@ -1793,14 +1769,9 @@ public override async Task Contains_with_local_list_closure_all_null(bool async) AssertSql( """ -@ids='[null,null]' (Size = 4000) - SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[CustomerID] IN ( - SELECT [i].[value] - FROM OPENJSON(@ids) WITH ([value] nchar(5) '$') AS [i] -) +WHERE 0 = 1 """); } @@ -1822,25 +1793,21 @@ public override async Task Contains_with_local_list_inline_closure_mix(bool asyn AssertSql( """ -@p='["ABCDE","ALFKI"]' (Size = 4000) +@p1='ABCDE' (Size = 5) (DbType = StringFixedLength) +@p2='ALFKI' (Size = 5) (DbType = StringFixedLength) SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[CustomerID] IN ( - SELECT [p].[value] - FROM OPENJSON(@p) WITH ([value] nchar(5) '$') AS [p] -) +WHERE [c].[CustomerID] IN (@p1, @p2) """, // """ -@p='["ABCDE","ANATR"]' (Size = 4000) +@p1='ABCDE' (Size = 5) (DbType = StringFixedLength) +@p2='ANATR' (Size = 5) (DbType = StringFixedLength) SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[CustomerID] IN ( - SELECT [p].[value] - FROM OPENJSON(@p) WITH ([value] nchar(5) '$') AS [p] -) +WHERE [c].[CustomerID] IN (@p1, @p2) """); } @@ -1850,25 +1817,21 @@ public override async Task Contains_with_local_non_primitive_list_inline_closure AssertSql( """ -@Select='["ABCDE","ALFKI"]' (Size = 4000) +@Select1='ABCDE' (Size = 5) (DbType = StringFixedLength) +@Select2='ALFKI' (Size = 5) (DbType = StringFixedLength) SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[CustomerID] IN ( - SELECT [s].[value] - FROM OPENJSON(@Select) WITH ([value] nchar(5) '$') AS [s] -) +WHERE [c].[CustomerID] IN (@Select1, @Select2) """, // """ -@Select='["ABCDE","ANATR"]' (Size = 4000) +@Select1='ABCDE' (Size = 5) (DbType = StringFixedLength) +@Select2='ANATR' (Size = 5) (DbType = StringFixedLength) SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[CustomerID] IN ( - SELECT [s].[value] - FROM OPENJSON(@Select) WITH ([value] nchar(5) '$') AS [s] -) +WHERE [c].[CustomerID] IN (@Select1, @Select2) """); } @@ -1878,25 +1841,20 @@ public override async Task Contains_with_local_enumerable_closure(bool async) AssertSql( """ -@ids='["ABCDE","ALFKI"]' (Size = 4000) +@ids1='ABCDE' (Size = 5) (DbType = StringFixedLength) +@ids2='ALFKI' (Size = 5) (DbType = StringFixedLength) SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[CustomerID] IN ( - SELECT [i].[value] - FROM OPENJSON(@ids) WITH ([value] nchar(5) '$') AS [i] -) +WHERE [c].[CustomerID] IN (@ids1, @ids2) """, // """ -@ids='["ABCDE"]' (Size = 4000) +@ids1='ABCDE' (Size = 5) (DbType = StringFixedLength) SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[CustomerID] IN ( - SELECT [i].[value] - FROM OPENJSON(@ids) WITH ([value] nchar(5) '$') AS [i] -) +WHERE [c].[CustomerID] = @ids1 """); } @@ -1906,14 +1864,12 @@ public override async Task Contains_with_local_object_enumerable_closure(bool as AssertSql( """ -@ids='["ABCDE","ALFKI"]' (Size = 4000) +@ids1='ABCDE' (Size = 5) (DbType = StringFixedLength) +@ids2='ALFKI' (Size = 5) (DbType = StringFixedLength) SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[CustomerID] IN ( - SELECT [i].[value] - FROM OPENJSON(@ids) WITH ([value] nchar(5) '$') AS [i] -) +WHERE [c].[CustomerID] IN (@ids1, @ids2) """); } @@ -1923,14 +1879,9 @@ public override async Task Contains_with_local_enumerable_closure_all_null(bool AssertSql( """ -@ids='[]' (Size = 4000) - SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[CustomerID] IN ( - SELECT [i].[value] - FROM OPENJSON(@ids) WITH ([value] nchar(5) '$') AS [i] -) +WHERE 0 = 1 """); } @@ -1960,25 +1911,20 @@ public override async Task Contains_with_local_ordered_enumerable_closure(bool a AssertSql( """ -@ids='["ABCDE","ALFKI"]' (Size = 4000) +@ids1='ABCDE' (Size = 5) (DbType = StringFixedLength) +@ids2='ALFKI' (Size = 5) (DbType = StringFixedLength) SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[CustomerID] IN ( - SELECT [i].[value] - FROM OPENJSON(@ids) WITH ([value] nchar(5) '$') AS [i] -) +WHERE [c].[CustomerID] IN (@ids1, @ids2) """, // """ -@ids='["ABCDE"]' (Size = 4000) +@ids1='ABCDE' (Size = 5) (DbType = StringFixedLength) SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[CustomerID] IN ( - SELECT [i].[value] - FROM OPENJSON(@ids) WITH ([value] nchar(5) '$') AS [i] -) +WHERE [c].[CustomerID] = @ids1 """); } @@ -1988,14 +1934,12 @@ public override async Task Contains_with_local_object_ordered_enumerable_closure AssertSql( """ -@ids='["ABCDE","ALFKI"]' (Size = 4000) +@ids1='ABCDE' (Size = 5) (DbType = StringFixedLength) +@ids2='ALFKI' (Size = 5) (DbType = StringFixedLength) SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[CustomerID] IN ( - SELECT [i].[value] - FROM OPENJSON(@ids) WITH ([value] nchar(5) '$') AS [i] -) +WHERE [c].[CustomerID] IN (@ids1, @ids2) """); } @@ -2005,14 +1949,9 @@ public override async Task Contains_with_local_ordered_enumerable_closure_all_nu AssertSql( """ -@ids='[null,null]' (Size = 4000) - SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[CustomerID] IN ( - SELECT [i].[value] - FROM OPENJSON(@ids) WITH ([value] nchar(5) '$') AS [i] -) +WHERE 0 = 1 """); } @@ -2034,25 +1973,21 @@ public override async Task Contains_with_local_ordered_enumerable_inline_closure AssertSql( """ -@Order='["ABCDE","ALFKI"]' (Size = 4000) +@Order1='ABCDE' (Size = 5) (DbType = StringFixedLength) +@Order2='ALFKI' (Size = 5) (DbType = StringFixedLength) SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[CustomerID] IN ( - SELECT [o].[value] - FROM OPENJSON(@Order) WITH ([value] nchar(5) '$') AS [o] -) +WHERE [c].[CustomerID] IN (@Order1, @Order2) """, // """ -@Order='["ABCDE","ANATR"]' (Size = 4000) +@Order1='ABCDE' (Size = 5) (DbType = StringFixedLength) +@Order2='ANATR' (Size = 5) (DbType = StringFixedLength) SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[CustomerID] IN ( - SELECT [o].[value] - FROM OPENJSON(@Order) WITH ([value] nchar(5) '$') AS [o] -) +WHERE [c].[CustomerID] IN (@Order1, @Order2) """); } @@ -2062,25 +1997,20 @@ public override async Task Contains_with_local_read_only_collection_closure(bool AssertSql( """ -@ids='["ABCDE","ALFKI"]' (Size = 4000) +@ids1='ABCDE' (Size = 5) (DbType = StringFixedLength) +@ids2='ALFKI' (Size = 5) (DbType = StringFixedLength) SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[CustomerID] IN ( - SELECT [i].[value] - FROM OPENJSON(@ids) WITH ([value] nchar(5) '$') AS [i] -) +WHERE [c].[CustomerID] IN (@ids1, @ids2) """, // """ -@ids='["ABCDE"]' (Size = 4000) +@ids1='ABCDE' (Size = 5) (DbType = StringFixedLength) SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[CustomerID] IN ( - SELECT [i].[value] - FROM OPENJSON(@ids) WITH ([value] nchar(5) '$') AS [i] -) +WHERE [c].[CustomerID] = @ids1 """); } @@ -2090,14 +2020,12 @@ public override async Task Contains_with_local_object_read_only_collection_closu AssertSql( """ -@ids='["ABCDE","ALFKI"]' (Size = 4000) +@ids1='ABCDE' (Size = 5) (DbType = StringFixedLength) +@ids2='ALFKI' (Size = 5) (DbType = StringFixedLength) SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[CustomerID] IN ( - SELECT [i].[value] - FROM OPENJSON(@ids) WITH ([value] nchar(5) '$') AS [i] -) +WHERE [c].[CustomerID] IN (@ids1, @ids2) """); } @@ -2107,14 +2035,9 @@ public override async Task Contains_with_local_ordered_read_only_collection_all_ AssertSql( """ -@ids='[null,null]' (Size = 4000) - SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[CustomerID] IN ( - SELECT [i].[value] - FROM OPENJSON(@ids) WITH ([value] nchar(5) '$') AS [i] -) +WHERE 0 = 1 """); } @@ -2136,25 +2059,21 @@ public override async Task Contains_with_local_read_only_collection_inline_closu AssertSql( """ -@AsReadOnly='["ABCDE","ALFKI"]' (Size = 4000) +@AsReadOnly1='ABCDE' (Size = 5) (DbType = StringFixedLength) +@AsReadOnly2='ALFKI' (Size = 5) (DbType = StringFixedLength) SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[CustomerID] IN ( - SELECT [a].[value] - FROM OPENJSON(@AsReadOnly) WITH ([value] nchar(5) '$') AS [a] -) +WHERE [c].[CustomerID] IN (@AsReadOnly1, @AsReadOnly2) """, // """ -@AsReadOnly='["ABCDE","ANATR"]' (Size = 4000) +@AsReadOnly1='ABCDE' (Size = 5) (DbType = StringFixedLength) +@AsReadOnly2='ANATR' (Size = 5) (DbType = StringFixedLength) SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[CustomerID] IN ( - SELECT [a].[value] - FROM OPENJSON(@AsReadOnly) WITH ([value] nchar(5) '$') AS [a] -) +WHERE [c].[CustomerID] IN (@AsReadOnly1, @AsReadOnly2) """); } @@ -2164,14 +2083,12 @@ public override async Task Contains_with_local_non_primitive_list_closure_mix(bo AssertSql( """ -@Select='["ABCDE","ALFKI"]' (Size = 4000) +@Select1='ABCDE' (Size = 5) (DbType = StringFixedLength) +@Select2='ALFKI' (Size = 5) (DbType = StringFixedLength) SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[CustomerID] IN ( - SELECT [s].[value] - FROM OPENJSON(@Select) WITH ([value] nchar(5) '$') AS [s] -) +WHERE [c].[CustomerID] IN (@Select1, @Select2) """); } @@ -2181,14 +2098,12 @@ public override async Task Contains_with_local_collection_false(bool async) AssertSql( """ -@ids='["ABCDE","ALFKI"]' (Size = 4000) +@ids1='ABCDE' (Size = 5) (DbType = StringFixedLength) +@ids2='ALFKI' (Size = 5) (DbType = StringFixedLength) SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[CustomerID] NOT IN ( - SELECT [i].[value] - FROM OPENJSON(@ids) WITH ([value] nchar(5) '$') AS [i] -) +WHERE [c].[CustomerID] NOT IN (@ids1, @ids2) """); } @@ -2198,14 +2113,12 @@ public override async Task Contains_with_local_collection_complex_predicate_and( AssertSql( """ -@ids='["ABCDE","ALFKI"]' (Size = 4000) +@ids1='ABCDE' (Size = 5) (DbType = StringFixedLength) +@ids2='ALFKI' (Size = 5) (DbType = StringFixedLength) SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[CustomerID] IN (N'ALFKI', N'ABCDE') AND [c].[CustomerID] IN ( - SELECT [i].[value] - FROM OPENJSON(@ids) WITH ([value] nchar(5) '$') AS [i] -) +WHERE [c].[CustomerID] IN (N'ALFKI', N'ABCDE') AND [c].[CustomerID] IN (@ids1, @ids2) """); } @@ -2239,14 +2152,12 @@ public override async Task Contains_with_local_collection_sql_injection(bool asy AssertSql( """ -@ids='["ALFKI","ABC\u0027)); GO; DROP TABLE Orders; GO; --"]' (Size = 4000) +@ids1='ALFKI' (Size = 5) (DbType = StringFixedLength) +@ids2='ABC')); GO; DROP TABLE Orders; GO; --' (Size = 4000) SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[CustomerID] IN ( - SELECT [i].[value] - FROM OPENJSON(@ids) WITH ([value] nchar(5) '$') AS [i] -) OR [c].[CustomerID] IN (N'ALFKI', N'ABCDE') +WHERE [c].[CustomerID] IN (@ids1, @ids2) OR [c].[CustomerID] IN (N'ALFKI', N'ABCDE') """); } @@ -2256,14 +2167,9 @@ public override async Task Contains_with_local_collection_empty_closure(bool asy AssertSql( """ -@ids='[]' (Size = 4000) - SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[CustomerID] IN ( - SELECT [i].[value] - FROM OPENJSON(@ids) WITH ([value] nchar(5) '$') AS [i] -) +WHERE 0 = 1 """); } @@ -2461,9 +2367,12 @@ public override async Task List_Contains_with_parameter_list(bool async) AssertSql( """ +@entity_equality_customers_CustomerID1='ALFKI' (Size = 5) (DbType = StringFixedLength) +@entity_equality_customers_CustomerID2='ANATR' (Size = 5) (DbType = StringFixedLength) + SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[CustomerID] IN (N'ALFKI', N'ANATR') +WHERE [c].[CustomerID] IN (@entity_equality_customers_CustomerID1, @entity_equality_customers_CustomerID2) """); } @@ -2473,9 +2382,12 @@ public override async Task Contains_with_parameter_list_value_type_id(bool async AssertSql( """ +@entity_equality_orders_OrderID1='10248' +@entity_equality_orders_OrderID2='10249' + SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] FROM [Orders] AS [o] -WHERE [o].[OrderID] IN (10248, 10249) +WHERE [o].[OrderID] IN (@entity_equality_orders_OrderID1, @entity_equality_orders_OrderID2) """); } @@ -2497,9 +2409,11 @@ public override async Task IImmutableSet_Contains_with_parameter(bool async) AssertSql( """ +@ids1='ALFKI' (Size = 5) (DbType = StringFixedLength) + SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[CustomerID] = N'ALFKI' +WHERE [c].[CustomerID] = @ids1 """); } @@ -2509,9 +2423,11 @@ public override async Task IReadOnlySet_Contains_with_parameter(bool async) AssertSql( """ +@ids1='ALFKI' (Size = 5) (DbType = StringFixedLength) + SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[CustomerID] = N'ALFKI' +WHERE [c].[CustomerID] = @ids1 """); } @@ -2521,14 +2437,11 @@ public override async Task HashSet_Contains_with_parameter(bool async) AssertSql( """ -@ids='["ALFKI"]' (Size = 4000) +@ids1='ALFKI' (Size = 5) (DbType = StringFixedLength) SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[CustomerID] IN ( - SELECT [i].[value] - FROM OPENJSON(@ids) WITH ([value] nchar(5) '$') AS [i] -) +WHERE [c].[CustomerID] = @ids1 """); } @@ -2538,14 +2451,11 @@ public override async Task ImmutableHashSet_Contains_with_parameter(bool async) AssertSql( """ -@ids='["ALFKI"]' (Size = 4000) +@ids1='ALFKI' (Size = 5) (DbType = StringFixedLength) SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[CustomerID] IN ( - SELECT [i].[value] - FROM OPENJSON(@ids) WITH ([value] nchar(5) '$') AS [i] -) +WHERE [c].[CustomerID] = @ids1 """); } @@ -2723,14 +2633,13 @@ public override async Task Where_subquery_any_equals_operator(bool async) AssertSql( """ -@ids='["ABCDE","ALFKI","ANATR"]' (Size = 4000) +@ids1='ABCDE' (Size = 5) (DbType = StringFixedLength) +@ids2='ALFKI' (Size = 5) (DbType = StringFixedLength) +@ids3='ANATR' (Size = 5) (DbType = StringFixedLength) SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[CustomerID] IN ( - SELECT [i].[value] - FROM OPENJSON(@ids) WITH ([value] nchar(5) '$') AS [i] -) +WHERE [c].[CustomerID] IN (@ids1, @ids2, @ids3) """); } @@ -2752,14 +2661,13 @@ public override async Task Where_subquery_any_equals_static(bool async) AssertSql( """ -@ids='["ABCDE","ALFKI","ANATR"]' (Size = 4000) +@ids1='ABCDE' (Size = 5) (DbType = StringFixedLength) +@ids2='ALFKI' (Size = 5) (DbType = StringFixedLength) +@ids3='ANATR' (Size = 5) (DbType = StringFixedLength) SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[CustomerID] IN ( - SELECT [i].[value] - FROM OPENJSON(@ids) WITH ([value] nchar(5) '$') AS [i] -) +WHERE [c].[CustomerID] IN (@ids1, @ids2, @ids3) """); } @@ -2769,25 +2677,23 @@ public override async Task Where_subquery_where_any(bool async) AssertSql( """ -@ids='["ABCDE","ALFKI","ANATR"]' (Size = 4000) +@ids1='ABCDE' (Size = 5) (DbType = StringFixedLength) +@ids2='ALFKI' (Size = 5) (DbType = StringFixedLength) +@ids3='ANATR' (Size = 5) (DbType = StringFixedLength) SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[City] = N'México D.F.' AND [c].[CustomerID] IN ( - SELECT [i].[value] - FROM OPENJSON(@ids) WITH ([value] nchar(5) '$') AS [i] -) +WHERE [c].[City] = N'México D.F.' AND [c].[CustomerID] IN (@ids1, @ids2, @ids3) """, // """ -@ids='["ABCDE","ALFKI","ANATR"]' (Size = 4000) +@ids1='ABCDE' (Size = 5) (DbType = StringFixedLength) +@ids2='ALFKI' (Size = 5) (DbType = StringFixedLength) +@ids3='ANATR' (Size = 5) (DbType = StringFixedLength) SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[City] = N'México D.F.' AND [c].[CustomerID] IN ( - SELECT [i].[value] - FROM OPENJSON(@ids) WITH ([value] nchar(5) '$') AS [i] -) +WHERE [c].[City] = N'México D.F.' AND [c].[CustomerID] IN (@ids1, @ids2, @ids3) """); } @@ -2797,14 +2703,13 @@ public override async Task Where_subquery_all_not_equals_operator(bool async) AssertSql( """ -@ids='["ABCDE","ALFKI","ANATR"]' (Size = 4000) +@ids1='ABCDE' (Size = 5) (DbType = StringFixedLength) +@ids2='ALFKI' (Size = 5) (DbType = StringFixedLength) +@ids3='ANATR' (Size = 5) (DbType = StringFixedLength) SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[CustomerID] NOT IN ( - SELECT [i].[value] - FROM OPENJSON(@ids) WITH ([value] nchar(5) '$') AS [i] -) +WHERE [c].[CustomerID] NOT IN (@ids1, @ids2, @ids3) """); } @@ -2826,14 +2731,13 @@ public override async Task Where_subquery_all_not_equals_static(bool async) AssertSql( """ -@ids='["ABCDE","ALFKI","ANATR"]' (Size = 4000) +@ids1='ABCDE' (Size = 5) (DbType = StringFixedLength) +@ids2='ALFKI' (Size = 5) (DbType = StringFixedLength) +@ids3='ANATR' (Size = 5) (DbType = StringFixedLength) SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[CustomerID] NOT IN ( - SELECT [i].[value] - FROM OPENJSON(@ids) WITH ([value] nchar(5) '$') AS [i] -) +WHERE [c].[CustomerID] NOT IN (@ids1, @ids2, @ids3) """); } @@ -2843,25 +2747,23 @@ public override async Task Where_subquery_where_all(bool async) AssertSql( """ -@ids='["ABCDE","ALFKI","ANATR"]' (Size = 4000) +@ids1='ABCDE' (Size = 5) (DbType = StringFixedLength) +@ids2='ALFKI' (Size = 5) (DbType = StringFixedLength) +@ids3='ANATR' (Size = 5) (DbType = StringFixedLength) SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[City] = N'México D.F.' AND [c].[CustomerID] NOT IN ( - SELECT [i].[value] - FROM OPENJSON(@ids) WITH ([value] nchar(5) '$') AS [i] -) +WHERE [c].[City] = N'México D.F.' AND [c].[CustomerID] NOT IN (@ids1, @ids2, @ids3) """, // """ -@ids='["ABCDE","ALFKI","ANATR"]' (Size = 4000) +@ids1='ABCDE' (Size = 5) (DbType = StringFixedLength) +@ids2='ALFKI' (Size = 5) (DbType = StringFixedLength) +@ids3='ANATR' (Size = 5) (DbType = StringFixedLength) SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[City] = N'México D.F.' AND [c].[CustomerID] NOT IN ( - SELECT [i].[value] - FROM OPENJSON(@ids) WITH ([value] nchar(5) '$') AS [i] -) +WHERE [c].[City] = N'México D.F.' AND [c].[CustomerID] NOT IN (@ids1, @ids2, @ids3) """); } @@ -3026,16 +2928,14 @@ public override async Task Contains_inside_aggregate_function_with_GroupBy(bool AssertSql( """ -@cities='["London","Berlin"]' (Size = 4000) +@cities1='London' (Size = 15) +@cities2='Berlin' (Size = 15) SELECT COUNT([s].[value]) FROM [Customers] AS [c] OUTER APPLY ( SELECT CASE - WHEN [c].[City] IN ( - SELECT [c0].[value] - FROM OPENJSON(@cities) WITH ([value] nvarchar(15) '$') AS [c0] - ) THEN 1 + WHEN [c].[City] IN (@cities1, @cities2) THEN 1 END AS [value] ) AS [s] GROUP BY [c].[Country] @@ -3048,16 +2948,14 @@ public override async Task Contains_inside_Average_without_GroupBy(bool async) AssertSql( """ -@cities='["London","Berlin"]' (Size = 4000) +@cities1='London' (Size = 15) +@cities2='Berlin' (Size = 15) SELECT AVG([s].[value]) FROM [Customers] AS [c] OUTER APPLY ( SELECT CASE - WHEN [c].[City] IN ( - SELECT [c0].[value] - FROM OPENJSON(@cities) WITH ([value] nvarchar(15) '$') AS [c0] - ) THEN 1.0E0 + WHEN [c].[City] IN (@cities1, @cities2) THEN 1.0E0 ELSE 0.0E0 END AS [value] ) AS [s] @@ -3070,16 +2968,14 @@ public override async Task Contains_inside_Sum_without_GroupBy(bool async) AssertSql( """ -@cities='["London","Berlin"]' (Size = 4000) +@cities1='London' (Size = 15) +@cities2='Berlin' (Size = 15) SELECT ISNULL(SUM([s].[value]), 0) FROM [Customers] AS [c] OUTER APPLY ( SELECT CASE - WHEN [c].[City] IN ( - SELECT [c0].[value] - FROM OPENJSON(@cities) WITH ([value] nvarchar(15) '$') AS [c0] - ) THEN 1 + WHEN [c].[City] IN (@cities1, @cities2) THEN 1 ELSE 0 END AS [value] ) AS [s] @@ -3092,14 +2988,12 @@ public override async Task Contains_inside_Count_without_GroupBy(bool async) AssertSql( """ -@cities='["London","Berlin"]' (Size = 4000) +@cities1='London' (Size = 15) +@cities2='Berlin' (Size = 15) SELECT COUNT(*) FROM [Customers] AS [c] -WHERE [c].[City] IN ( - SELECT [c0].[value] - FROM OPENJSON(@cities) WITH ([value] nvarchar(15) '$') AS [c0] -) +WHERE [c].[City] IN (@cities1, @cities2) """); } @@ -3109,14 +3003,12 @@ public override async Task Contains_inside_LongCount_without_GroupBy(bool async) AssertSql( """ -@cities='["London","Berlin"]' (Size = 4000) +@cities1='London' (Size = 15) +@cities2='Berlin' (Size = 15) SELECT COUNT_BIG(*) FROM [Customers] AS [c] -WHERE [c].[City] IN ( - SELECT [c0].[value] - FROM OPENJSON(@cities) WITH ([value] nvarchar(15) '$') AS [c0] -) +WHERE [c].[City] IN (@cities1, @cities2) """); } @@ -3126,16 +3018,14 @@ public override async Task Contains_inside_Max_without_GroupBy(bool async) AssertSql( """ -@cities='["London","Berlin"]' (Size = 4000) +@cities1='London' (Size = 15) +@cities2='Berlin' (Size = 15) SELECT MAX([s].[value]) FROM [Customers] AS [c] OUTER APPLY ( SELECT CASE - WHEN [c].[City] IN ( - SELECT [c0].[value] - FROM OPENJSON(@cities) WITH ([value] nvarchar(15) '$') AS [c0] - ) THEN 1 + WHEN [c].[City] IN (@cities1, @cities2) THEN 1 ELSE 0 END AS [value] ) AS [s] @@ -3148,16 +3038,14 @@ public override async Task Contains_inside_Min_without_GroupBy(bool async) AssertSql( """ -@cities='["London","Berlin"]' (Size = 4000) +@cities1='London' (Size = 15) +@cities2='Berlin' (Size = 15) SELECT MIN([s].[value]) FROM [Customers] AS [c] OUTER APPLY ( SELECT CASE - WHEN [c].[City] IN ( - SELECT [c0].[value] - FROM OPENJSON(@cities) WITH ([value] nvarchar(15) '$') AS [c0] - ) THEN 1 + WHEN [c].[City] IN (@cities1, @cities2) THEN 1 ELSE 0 END AS [value] ) AS [s] diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindCompiledQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindCompiledQuerySqlServerTest.cs index b4d340b0f86..40b5787447d 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindCompiledQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindCompiledQuerySqlServerTest.cs @@ -178,25 +178,19 @@ public override void Query_with_contains() AssertSql( """ -@args='["ALFKI"]' (Size = 4000) +@args1='ALFKI' (Size = 5) (DbType = StringFixedLength) SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[CustomerID] IN ( - SELECT [a].[value] - FROM OPENJSON(@args) WITH ([value] nchar(5) '$') AS [a] -) +WHERE [c].[CustomerID] = @args1 """, // """ -@args='["ANATR"]' (Size = 4000) +@args1='ANATR' (Size = 5) (DbType = StringFixedLength) SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[CustomerID] IN ( - SELECT [a].[value] - FROM OPENJSON(@args) WITH ([value] nchar(5) '$') AS [a] -) +WHERE [c].[CustomerID] = @args1 """); } @@ -401,19 +395,27 @@ public override void Query_with_array_parameter() AssertSql( """ -@args='["ALFKI"]' (Size = 4000) +@args1='ALFKI' (Size = 5) (DbType = StringFixedLength) SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[CustomerID] = JSON_VALUE(@args, '$[0]') +WHERE [c].[CustomerID] = ( + SELECT [a].[Value] + FROM (VALUES (1, @args1)) AS [a]([_ord], [Value]) + ORDER BY [a].[_ord] + OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY) """, // """ -@args='["ANATR"]' (Size = 4000) +@args1='ANATR' (Size = 5) (DbType = StringFixedLength) SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[CustomerID] = JSON_VALUE(@args, '$[0]') +WHERE [c].[CustomerID] = ( + SELECT [a].[Value] + FROM (VALUES (1, @args1)) AS [a]([_ord], [Value]) + ORDER BY [a].[_ord] + OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY) """); } @@ -423,19 +425,27 @@ public override async Task Query_with_array_parameter_async() AssertSql( """ -@args='["ALFKI"]' (Size = 4000) +@args1='ALFKI' (Size = 5) (DbType = StringFixedLength) SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[CustomerID] = JSON_VALUE(@args, '$[0]') +WHERE [c].[CustomerID] = ( + SELECT [a].[Value] + FROM (VALUES (1, @args1)) AS [a]([_ord], [Value]) + ORDER BY [a].[_ord] + OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY) """, // """ -@args='["ANATR"]' (Size = 4000) +@args1='ANATR' (Size = 5) (DbType = StringFixedLength) SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[CustomerID] = JSON_VALUE(@args, '$[0]') +WHERE [c].[CustomerID] = ( + SELECT [a].[Value] + FROM (VALUES (1, @args1)) AS [a]([_ord], [Value]) + ORDER BY [a].[_ord] + OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY) """); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindEFPropertyIncludeQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindEFPropertyIncludeQuerySqlServerTest.cs index ff2faffb210..1b6275c2757 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindEFPropertyIncludeQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindEFPropertyIncludeQuerySqlServerTest.cs @@ -630,25 +630,19 @@ public override async Task Include_collection_OrderBy_list_does_not_contains(boo AssertSql( """ -@list='["ALFKI"]' (Size = 4000) +@list1='ALFKI' (Size = 5) (DbType = StringFixedLength) @p='1' SELECT [c0].[CustomerID], [c0].[Address], [c0].[City], [c0].[CompanyName], [c0].[ContactName], [c0].[ContactTitle], [c0].[Country], [c0].[Fax], [c0].[Phone], [c0].[PostalCode], [c0].[Region], [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] FROM ( SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], CASE - WHEN [c].[CustomerID] NOT IN ( - SELECT [l].[value] - FROM OPENJSON(@list) WITH ([value] nchar(5) '$') AS [l] - ) THEN CAST(1 AS bit) + WHEN [c].[CustomerID] <> @list1 THEN CAST(1 AS bit) ELSE CAST(0 AS bit) END AS [c] FROM [Customers] AS [c] WHERE [c].[CustomerID] LIKE N'A%' ORDER BY CASE - WHEN [c].[CustomerID] NOT IN ( - SELECT [l].[value] - FROM OPENJSON(@list) WITH ([value] nchar(5) '$') AS [l] - ) THEN CAST(1 AS bit) + WHEN [c].[CustomerID] <> @list1 THEN CAST(1 AS bit) ELSE CAST(0 AS bit) END OFFSET @p ROWS @@ -978,27 +972,14 @@ public override async Task Include_collection_OrderBy_empty_list_contains(bool a AssertSql( """ -@list='[]' (Size = 4000) @p='1' SELECT [c0].[CustomerID], [c0].[Address], [c0].[City], [c0].[CompanyName], [c0].[ContactName], [c0].[ContactTitle], [c0].[Country], [c0].[Fax], [c0].[Phone], [c0].[PostalCode], [c0].[Region], [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] FROM ( - SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], CASE - WHEN [c].[CustomerID] IN ( - SELECT [l].[value] - FROM OPENJSON(@list) WITH ([value] nchar(5) '$') AS [l] - ) THEN CAST(1 AS bit) - ELSE CAST(0 AS bit) - END AS [c] + SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], CAST(0 AS bit) AS [c] FROM [Customers] AS [c] WHERE [c].[CustomerID] LIKE N'A%' - ORDER BY CASE - WHEN [c].[CustomerID] IN ( - SELECT [l].[value] - FROM OPENJSON(@list) WITH ([value] nchar(5) '$') AS [l] - ) THEN CAST(1 AS bit) - ELSE CAST(0 AS bit) - END + ORDER BY (SELECT 1) OFFSET @p ROWS ) AS [c0] LEFT JOIN [Orders] AS [o] ON [c0].[CustomerID] = [o].[CustomerID] @@ -1374,25 +1355,19 @@ public override async Task Include_collection_OrderBy_list_contains(bool async) AssertSql( """ -@list='["ALFKI"]' (Size = 4000) +@list1='ALFKI' (Size = 5) (DbType = StringFixedLength) @p='1' SELECT [c0].[CustomerID], [c0].[Address], [c0].[City], [c0].[CompanyName], [c0].[ContactName], [c0].[ContactTitle], [c0].[Country], [c0].[Fax], [c0].[Phone], [c0].[PostalCode], [c0].[Region], [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] FROM ( SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], CASE - WHEN [c].[CustomerID] IN ( - SELECT [l].[value] - FROM OPENJSON(@list) WITH ([value] nchar(5) '$') AS [l] - ) THEN CAST(1 AS bit) + WHEN [c].[CustomerID] = @list1 THEN CAST(1 AS bit) ELSE CAST(0 AS bit) END AS [c] FROM [Customers] AS [c] WHERE [c].[CustomerID] LIKE N'A%' ORDER BY CASE - WHEN [c].[CustomerID] IN ( - SELECT [l].[value] - FROM OPENJSON(@list) WITH ([value] nchar(5) '$') AS [l] - ) THEN CAST(1 AS bit) + WHEN [c].[CustomerID] = @list1 THEN CAST(1 AS bit) ELSE CAST(0 AS bit) END OFFSET @p ROWS @@ -1853,27 +1828,14 @@ public override async Task Include_collection_OrderBy_empty_list_does_not_contai AssertSql( """ -@list='[]' (Size = 4000) @p='1' SELECT [c0].[CustomerID], [c0].[Address], [c0].[City], [c0].[CompanyName], [c0].[ContactName], [c0].[ContactTitle], [c0].[Country], [c0].[Fax], [c0].[Phone], [c0].[PostalCode], [c0].[Region], [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] FROM ( - SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], CASE - WHEN [c].[CustomerID] NOT IN ( - SELECT [l].[value] - FROM OPENJSON(@list) WITH ([value] nchar(5) '$') AS [l] - ) THEN CAST(1 AS bit) - ELSE CAST(0 AS bit) - END AS [c] + SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], CAST(1 AS bit) AS [c] FROM [Customers] AS [c] WHERE [c].[CustomerID] LIKE N'A%' - ORDER BY CASE - WHEN [c].[CustomerID] NOT IN ( - SELECT [l].[value] - FROM OPENJSON(@list) WITH ([value] nchar(5) '$') AS [l] - ) THEN CAST(1 AS bit) - ELSE CAST(0 AS bit) - END + ORDER BY (SELECT 1) OFFSET @p ROWS ) AS [c0] LEFT JOIN [Orders] AS [o] ON [c0].[CustomerID] = [o].[CustomerID] diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindIncludeNoTrackingQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindIncludeNoTrackingQuerySqlServerTest.cs index d213b26db6c..d90befd0c2b 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindIncludeNoTrackingQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindIncludeNoTrackingQuerySqlServerTest.cs @@ -169,25 +169,19 @@ public override async Task Include_collection_OrderBy_list_does_not_contains(boo AssertSql( """ -@list='["ALFKI"]' (Size = 4000) +@list1='ALFKI' (Size = 5) (DbType = StringFixedLength) @p='1' SELECT [c0].[CustomerID], [c0].[Address], [c0].[City], [c0].[CompanyName], [c0].[ContactName], [c0].[ContactTitle], [c0].[Country], [c0].[Fax], [c0].[Phone], [c0].[PostalCode], [c0].[Region], [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] FROM ( SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], CASE - WHEN [c].[CustomerID] NOT IN ( - SELECT [l].[value] - FROM OPENJSON(@list) WITH ([value] nchar(5) '$') AS [l] - ) THEN CAST(1 AS bit) + WHEN [c].[CustomerID] <> @list1 THEN CAST(1 AS bit) ELSE CAST(0 AS bit) END AS [c] FROM [Customers] AS [c] WHERE [c].[CustomerID] LIKE N'A%' ORDER BY CASE - WHEN [c].[CustomerID] NOT IN ( - SELECT [l].[value] - FROM OPENJSON(@list) WITH ([value] nchar(5) '$') AS [l] - ) THEN CAST(1 AS bit) + WHEN [c].[CustomerID] <> @list1 THEN CAST(1 AS bit) ELSE CAST(0 AS bit) END OFFSET @p ROWS @@ -451,25 +445,19 @@ public override async Task Include_collection_OrderBy_list_contains(bool async) AssertSql( """ -@list='["ALFKI"]' (Size = 4000) +@list1='ALFKI' (Size = 5) (DbType = StringFixedLength) @p='1' SELECT [c0].[CustomerID], [c0].[Address], [c0].[City], [c0].[CompanyName], [c0].[ContactName], [c0].[ContactTitle], [c0].[Country], [c0].[Fax], [c0].[Phone], [c0].[PostalCode], [c0].[Region], [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] FROM ( SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], CASE - WHEN [c].[CustomerID] IN ( - SELECT [l].[value] - FROM OPENJSON(@list) WITH ([value] nchar(5) '$') AS [l] - ) THEN CAST(1 AS bit) + WHEN [c].[CustomerID] = @list1 THEN CAST(1 AS bit) ELSE CAST(0 AS bit) END AS [c] FROM [Customers] AS [c] WHERE [c].[CustomerID] LIKE N'A%' ORDER BY CASE - WHEN [c].[CustomerID] IN ( - SELECT [l].[value] - FROM OPENJSON(@list) WITH ([value] nchar(5) '$') AS [l] - ) THEN CAST(1 AS bit) + WHEN [c].[CustomerID] = @list1 THEN CAST(1 AS bit) ELSE CAST(0 AS bit) END OFFSET @p ROWS @@ -2067,27 +2055,14 @@ public override async Task Include_collection_OrderBy_empty_list_contains(bool a AssertSql( """ -@list='[]' (Size = 4000) @p='1' SELECT [c0].[CustomerID], [c0].[Address], [c0].[City], [c0].[CompanyName], [c0].[ContactName], [c0].[ContactTitle], [c0].[Country], [c0].[Fax], [c0].[Phone], [c0].[PostalCode], [c0].[Region], [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] FROM ( - SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], CASE - WHEN [c].[CustomerID] IN ( - SELECT [l].[value] - FROM OPENJSON(@list) WITH ([value] nchar(5) '$') AS [l] - ) THEN CAST(1 AS bit) - ELSE CAST(0 AS bit) - END AS [c] + SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], CAST(0 AS bit) AS [c] FROM [Customers] AS [c] WHERE [c].[CustomerID] LIKE N'A%' - ORDER BY CASE - WHEN [c].[CustomerID] IN ( - SELECT [l].[value] - FROM OPENJSON(@list) WITH ([value] nchar(5) '$') AS [l] - ) THEN CAST(1 AS bit) - ELSE CAST(0 AS bit) - END + ORDER BY (SELECT 1) OFFSET @p ROWS ) AS [c0] LEFT JOIN [Orders] AS [o] ON [c0].[CustomerID] = [o].[CustomerID] @@ -2101,27 +2076,14 @@ public override async Task Include_collection_OrderBy_empty_list_does_not_contai AssertSql( """ -@list='[]' (Size = 4000) @p='1' SELECT [c0].[CustomerID], [c0].[Address], [c0].[City], [c0].[CompanyName], [c0].[ContactName], [c0].[ContactTitle], [c0].[Country], [c0].[Fax], [c0].[Phone], [c0].[PostalCode], [c0].[Region], [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] FROM ( - SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], CASE - WHEN [c].[CustomerID] NOT IN ( - SELECT [l].[value] - FROM OPENJSON(@list) WITH ([value] nchar(5) '$') AS [l] - ) THEN CAST(1 AS bit) - ELSE CAST(0 AS bit) - END AS [c] + SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], CAST(1 AS bit) AS [c] FROM [Customers] AS [c] WHERE [c].[CustomerID] LIKE N'A%' - ORDER BY CASE - WHEN [c].[CustomerID] NOT IN ( - SELECT [l].[value] - FROM OPENJSON(@list) WITH ([value] nchar(5) '$') AS [l] - ) THEN CAST(1 AS bit) - ELSE CAST(0 AS bit) - END + ORDER BY (SELECT 1) OFFSET @p ROWS ) AS [c0] LEFT JOIN [Orders] AS [o] ON [c0].[CustomerID] = [o].[CustomerID] diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindIncludeQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindIncludeQuerySqlServerTest.cs index 938bfc83450..b61c3060e41 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindIncludeQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindIncludeQuerySqlServerTest.cs @@ -1516,27 +1516,14 @@ public override async Task Include_collection_OrderBy_empty_list_contains(bool a AssertSql( """ -@list='[]' (Size = 4000) @p='1' SELECT [c0].[CustomerID], [c0].[Address], [c0].[City], [c0].[CompanyName], [c0].[ContactName], [c0].[ContactTitle], [c0].[Country], [c0].[Fax], [c0].[Phone], [c0].[PostalCode], [c0].[Region], [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] FROM ( - SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], CASE - WHEN [c].[CustomerID] IN ( - SELECT [l].[value] - FROM OPENJSON(@list) WITH ([value] nchar(5) '$') AS [l] - ) THEN CAST(1 AS bit) - ELSE CAST(0 AS bit) - END AS [c] + SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], CAST(0 AS bit) AS [c] FROM [Customers] AS [c] WHERE [c].[CustomerID] LIKE N'A%' - ORDER BY CASE - WHEN [c].[CustomerID] IN ( - SELECT [l].[value] - FROM OPENJSON(@list) WITH ([value] nchar(5) '$') AS [l] - ) THEN CAST(1 AS bit) - ELSE CAST(0 AS bit) - END + ORDER BY (SELECT 1) OFFSET @p ROWS ) AS [c0] LEFT JOIN [Orders] AS [o] ON [c0].[CustomerID] = [o].[CustomerID] @@ -1550,27 +1537,14 @@ public override async Task Include_collection_OrderBy_empty_list_does_not_contai AssertSql( """ -@list='[]' (Size = 4000) @p='1' SELECT [c0].[CustomerID], [c0].[Address], [c0].[City], [c0].[CompanyName], [c0].[ContactName], [c0].[ContactTitle], [c0].[Country], [c0].[Fax], [c0].[Phone], [c0].[PostalCode], [c0].[Region], [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] FROM ( - SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], CASE - WHEN [c].[CustomerID] NOT IN ( - SELECT [l].[value] - FROM OPENJSON(@list) WITH ([value] nchar(5) '$') AS [l] - ) THEN CAST(1 AS bit) - ELSE CAST(0 AS bit) - END AS [c] + SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], CAST(1 AS bit) AS [c] FROM [Customers] AS [c] WHERE [c].[CustomerID] LIKE N'A%' - ORDER BY CASE - WHEN [c].[CustomerID] NOT IN ( - SELECT [l].[value] - FROM OPENJSON(@list) WITH ([value] nchar(5) '$') AS [l] - ) THEN CAST(1 AS bit) - ELSE CAST(0 AS bit) - END + ORDER BY (SELECT 1) OFFSET @p ROWS ) AS [c0] LEFT JOIN [Orders] AS [o] ON [c0].[CustomerID] = [o].[CustomerID] @@ -1584,25 +1558,19 @@ public override async Task Include_collection_OrderBy_list_contains(bool async) AssertSql( """ -@list='["ALFKI"]' (Size = 4000) +@list1='ALFKI' (Size = 5) (DbType = StringFixedLength) @p='1' SELECT [c0].[CustomerID], [c0].[Address], [c0].[City], [c0].[CompanyName], [c0].[ContactName], [c0].[ContactTitle], [c0].[Country], [c0].[Fax], [c0].[Phone], [c0].[PostalCode], [c0].[Region], [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] FROM ( SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], CASE - WHEN [c].[CustomerID] IN ( - SELECT [l].[value] - FROM OPENJSON(@list) WITH ([value] nchar(5) '$') AS [l] - ) THEN CAST(1 AS bit) + WHEN [c].[CustomerID] = @list1 THEN CAST(1 AS bit) ELSE CAST(0 AS bit) END AS [c] FROM [Customers] AS [c] WHERE [c].[CustomerID] LIKE N'A%' ORDER BY CASE - WHEN [c].[CustomerID] IN ( - SELECT [l].[value] - FROM OPENJSON(@list) WITH ([value] nchar(5) '$') AS [l] - ) THEN CAST(1 AS bit) + WHEN [c].[CustomerID] = @list1 THEN CAST(1 AS bit) ELSE CAST(0 AS bit) END OFFSET @p ROWS @@ -1618,25 +1586,19 @@ public override async Task Include_collection_OrderBy_list_does_not_contains(boo AssertSql( """ -@list='["ALFKI"]' (Size = 4000) +@list1='ALFKI' (Size = 5) (DbType = StringFixedLength) @p='1' SELECT [c0].[CustomerID], [c0].[Address], [c0].[City], [c0].[CompanyName], [c0].[ContactName], [c0].[ContactTitle], [c0].[Country], [c0].[Fax], [c0].[Phone], [c0].[PostalCode], [c0].[Region], [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] FROM ( SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], CASE - WHEN [c].[CustomerID] NOT IN ( - SELECT [l].[value] - FROM OPENJSON(@list) WITH ([value] nchar(5) '$') AS [l] - ) THEN CAST(1 AS bit) + WHEN [c].[CustomerID] <> @list1 THEN CAST(1 AS bit) ELSE CAST(0 AS bit) END AS [c] FROM [Customers] AS [c] WHERE [c].[CustomerID] LIKE N'A%' ORDER BY CASE - WHEN [c].[CustomerID] NOT IN ( - SELECT [l].[value] - FROM OPENJSON(@list) WITH ([value] nchar(5) '$') AS [l] - ) THEN CAST(1 AS bit) + WHEN [c].[CustomerID] <> @list1 THEN CAST(1 AS bit) ELSE CAST(0 AS bit) END OFFSET @p ROWS diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindJoinQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindJoinQuerySqlServerTest.cs index c4e3126bb1f..5af43749bcc 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindJoinQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindJoinQuerySqlServerTest.cs @@ -963,19 +963,20 @@ public override async Task Join_local_collection_int_closure_is_cached_correctly AssertSql( """ -@p='[1,2]' (Size = 4000) +@p1='1' +@p2='2' SELECT [e].[EmployeeID] FROM [Employees] AS [e] -INNER JOIN OPENJSON(@p) WITH ([value] int '$') AS [p] ON [e].[EmployeeID] = [p].[value] +INNER JOIN (VALUES (@p1), (@p2)) AS [p]([Value]) ON [e].[EmployeeID] = [p].[Value] """, // """ -@p='[3]' (Size = 4000) +@p1='3' SELECT [e].[EmployeeID] FROM [Employees] AS [e] -INNER JOIN OPENJSON(@p) WITH ([value] int '$') AS [p] ON [e].[EmployeeID] = [p].[value] +INNER JOIN (VALUES (@p1)) AS [p]([Value]) ON [e].[EmployeeID] = [p].[Value] """); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindMiscellaneousQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindMiscellaneousQuerySqlServerTest.cs index ca5d72b7221..493b72084f6 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindMiscellaneousQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindMiscellaneousQuerySqlServerTest.cs @@ -3829,25 +3829,20 @@ public override async Task Contains_with_DateTime_Date(bool async) AssertSql( """ -@dates='["1996-07-04T00:00:00","1996-07-16T00:00:00"]' (Size = 4000) +@dates1='1996-07-04T00:00:00.0000000' (DbType = DateTime) +@dates2='1996-07-16T00:00:00.0000000' (DbType = DateTime) SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] FROM [Orders] AS [o] -WHERE CONVERT(date, [o].[OrderDate]) IN ( - SELECT [d].[value] - FROM OPENJSON(@dates) WITH ([value] datetime '$') AS [d] -) +WHERE CONVERT(date, [o].[OrderDate]) IN (@dates1, @dates2) """, // """ -@dates='["1996-07-04T00:00:00"]' (Size = 4000) +@dates1='1996-07-04T00:00:00.0000000' (DbType = DateTime) SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] FROM [Orders] AS [o] -WHERE CONVERT(date, [o].[OrderDate]) IN ( - SELECT [d].[value] - FROM OPENJSON(@dates) WITH ([value] datetime '$') AS [d] -) +WHERE CONVERT(date, [o].[OrderDate]) = @dates1 """); } @@ -4868,17 +4863,8 @@ public override async Task OrderBy_empty_list_contains(bool async) AssertSql( """ -@list='[]' (Size = 4000) - SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -ORDER BY CASE - WHEN [c].[CustomerID] IN ( - SELECT [l].[value] - FROM OPENJSON(@list) WITH ([value] nchar(5) '$') AS [l] - ) THEN CAST(1 AS bit) - ELSE CAST(0 AS bit) -END """); } @@ -4888,17 +4874,8 @@ public override async Task OrderBy_empty_list_does_not_contains(bool async) AssertSql( """ -@list='[]' (Size = 4000) - SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -ORDER BY CASE - WHEN [c].[CustomerID] NOT IN ( - SELECT [l].[value] - FROM OPENJSON(@list) WITH ([value] nchar(5) '$') AS [l] - ) THEN CAST(1 AS bit) - ELSE CAST(0 AS bit) -END """); } @@ -5426,9 +5403,11 @@ public override async Task Entity_equality_contains_with_list_of_null(bool async AssertSql( """ +@entity_equality_customers_CustomerID1='ALFKI' (Size = 5) (DbType = StringFixedLength) + SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[CustomerID] = N'ALFKI' +WHERE [c].[CustomerID] = @entity_equality_customers_CustomerID1 """); } @@ -6075,15 +6054,17 @@ FROM [Customers] AS [c] """, // """ -@orderIds='[10643,10692,10702,10835,10952,11011]' (Size = 4000) +@orderIds1='10643' +@orderIds2='10692' +@orderIds3='10702' +@orderIds4='10835' +@orderIds5='10952' +@orderIds6='11011' SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Orders] AS [o] LEFT JOIN [Customers] AS [c] ON [o].[CustomerID] = [c].[CustomerID] -WHERE [o].[OrderID] IN ( - SELECT [o0].[value] - FROM OPENJSON(@orderIds) WITH ([value] int '$') AS [o0] -) +WHERE [o].[OrderID] IN (@orderIds1, @orderIds2, @orderIds3, @orderIds4, @orderIds5, @orderIds6) """); } @@ -7034,30 +7015,22 @@ public override async Task Parameter_collection_Contains_with_projection_and_ord AssertSql( """ -@ids='[10248,10249]' (Size = 4000) +@ids1='10248' +@ids2='10249' SELECT [o].[Quantity] AS [Key], ( SELECT MAX([o1].[OrderDate]) FROM [Order Details] AS [o0] INNER JOIN [Orders] AS [o1] ON [o0].[OrderID] = [o1].[OrderID] - WHERE [o0].[OrderID] IN ( - SELECT [i0].[value] - FROM OPENJSON(@ids) WITH ([value] int '$') AS [i0] - ) AND [o].[Quantity] = [o0].[Quantity]) AS [MaxTimestamp] + WHERE [o0].[OrderID] IN (@ids1, @ids2) AND [o].[Quantity] = [o0].[Quantity]) AS [MaxTimestamp] FROM [Order Details] AS [o] -WHERE [o].[OrderID] IN ( - SELECT [i].[value] - FROM OPENJSON(@ids) WITH ([value] int '$') AS [i] -) +WHERE [o].[OrderID] IN (@ids1, @ids2) GROUP BY [o].[Quantity] ORDER BY ( SELECT MAX([o1].[OrderDate]) FROM [Order Details] AS [o0] INNER JOIN [Orders] AS [o1] ON [o0].[OrderID] = [o1].[OrderID] - WHERE [o0].[OrderID] IN ( - SELECT [i0].[value] - FROM OPENJSON(@ids) WITH ([value] int '$') AS [i0] - ) AND [o].[Quantity] = [o0].[Quantity]) + WHERE [o0].[OrderID] IN (@ids1, @ids2) AND [o].[Quantity] = [o0].[Quantity]) """); } @@ -7067,14 +7040,12 @@ public override async Task Contains_over_concatenated_columns_with_different_siz AssertSql( """ -@data='["ALFKIAlfreds Futterkiste","ANATRAna Trujillo Emparedados y helados"]' (Size = 4000) +@data1='ALFKIAlfreds Futterkiste' (Size = 45) +@data2='ANATRAna Trujillo Emparedados y helados' (Size = 45) SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[CustomerID] + [c].[CompanyName] IN ( - SELECT [d].[value] - FROM OPENJSON(@data) WITH ([value] nvarchar(45) '$') AS [d] -) +WHERE [c].[CustomerID] + [c].[CompanyName] IN (@data1, @data2) """); } @@ -7084,14 +7055,13 @@ public override async Task Contains_over_concatenated_column_and_constant(bool a AssertSql( """ -@data='["ALFKISomeConstant","ANATRSomeConstant","ALFKIX"]' (Size = 4000) +@data1='ALFKISomeConstant' (Size = 4000) +@data2='ANATRSomeConstant' (Size = 4000) +@data3='ALFKIX' (Size = 4000) SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[CustomerID] + N'SomeConstant' IN ( - SELECT [d].[value] - FROM OPENJSON(@data) WITH ([value] nvarchar(max) '$') AS [d] -) +WHERE [c].[CustomerID] + N'SomeConstant' IN (@data1, @data2, @data3) """); } @@ -7101,15 +7071,15 @@ public override async Task Contains_over_concatenated_columns_both_fixed_length( AssertSql( """ -@data='["ALFKIALFKI","ALFKI","ANATRAna Trujillo Emparedados y helados","ANATRANATR"]' (Size = 4000) +@data1='ALFKIALFKI' (Size = 10) (DbType = StringFixedLength) +@data2='ALFKI' (Size = 10) +@data3='ANATRAna Trujillo Emparedados y helados' (Size = 4000) +@data4='ANATRANATR' (Size = 10) (DbType = StringFixedLength) SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] FROM [Orders] AS [o] LEFT JOIN [Customers] AS [c] ON [o].[CustomerID] = [c].[CustomerID] -WHERE COALESCE([o].[CustomerID], N'') + COALESCE([c].[CustomerID], N'') IN ( - SELECT [d].[value] - FROM OPENJSON(@data) WITH ([value] nchar(10) '$') AS [d] -) +WHERE COALESCE([o].[CustomerID], N'') + COALESCE([c].[CustomerID], N'') IN (@data1, @data2, @data3, @data4) """); } @@ -7120,14 +7090,13 @@ public override async Task Contains_over_concatenated_column_and_parameter(bool AssertSql( """ @someVariable='SomeVariable' (Size = 4000) -@data='["ALFKISomeVariable","ANATRSomeVariable","ALFKIX"]' (Size = 4000) +@data1='ALFKISomeVariable' (Size = 4000) +@data2='ANATRSomeVariable' (Size = 4000) +@data3='ALFKIX' (Size = 4000) SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[CustomerID] + @someVariable IN ( - SELECT [d].[value] - FROM OPENJSON(@data) WITH ([value] nvarchar(max) '$') AS [d] -) +WHERE [c].[CustomerID] + @someVariable IN (@data1, @data2, @data3) """); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindNavigationsQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindNavigationsQuerySqlServerTest.cs index e1f57a950f9..944b04970b8 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindNavigationsQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindNavigationsQuerySqlServerTest.cs @@ -773,7 +773,12 @@ public override async Task Collection_select_nav_prop_first_or_default_then_nav_ AssertSql( """ -@orderIds='[10643,10692,10702,10835,10952,11011]' (Size = 4000) +@orderIds1='10643' +@orderIds2='10692' +@orderIds3='10702' +@orderIds4='10835' +@orderIds5='10952' +@orderIds6='11011' SELECT [s0].[CustomerID], [s0].[Address], [s0].[City], [s0].[CompanyName], [s0].[ContactName], [s0].[ContactTitle], [s0].[Country], [s0].[Fax], [s0].[Phone], [s0].[PostalCode], [s0].[Region] FROM [Customers] AS [c] @@ -783,10 +788,7 @@ LEFT JOIN ( SELECT [c0].[CustomerID], [c0].[Address], [c0].[City], [c0].[CompanyName], [c0].[ContactName], [c0].[ContactTitle], [c0].[Country], [c0].[Fax], [c0].[Phone], [c0].[PostalCode], [c0].[Region], [o].[CustomerID] AS [CustomerID0], ROW_NUMBER() OVER(PARTITION BY [o].[CustomerID] ORDER BY [o].[OrderID], [c0].[CustomerID]) AS [row] FROM [Orders] AS [o] LEFT JOIN [Customers] AS [c0] ON [o].[CustomerID] = [c0].[CustomerID] - WHERE [o].[OrderID] IN ( - SELECT [o0].[value] - FROM OPENJSON(@orderIds) WITH ([value] int '$') AS [o0] - ) + WHERE [o].[OrderID] IN (@orderIds1, @orderIds2, @orderIds3, @orderIds4, @orderIds5, @orderIds6) ) AS [s] WHERE [s].[row] <= 1 ) AS [s0] ON [c].[CustomerID] = [s0].[CustomerID0] diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindSelectQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindSelectQuerySqlServerTest.cs index 4475683bd2b..19e391e8f6e 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindSelectQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindSelectQuerySqlServerTest.cs @@ -2144,7 +2144,9 @@ public override async Task Projecting_after_navigation_and_distinct(bool async) AssertSql( """ -@filteredOrderIds='[10248,10249,10250]' (Size = 4000) +@filteredOrderIds1='10248' +@filteredOrderIds2='10249' +@filteredOrderIds3='10250' SELECT [s].[CustomerID], [o1].[CustomerID], [o1].[OrderID], [o1].[OrderDate] FROM ( @@ -2155,10 +2157,7 @@ FROM [Orders] AS [o] OUTER APPLY ( SELECT [s].[CustomerID], [o0].[OrderID], [o0].[OrderDate] FROM [Orders] AS [o0] - WHERE [s].[CustomerID] IS NOT NULL AND [s].[CustomerID] = [o0].[CustomerID] AND [o0].[OrderID] IN ( - SELECT [f].[value] - FROM OPENJSON(@filteredOrderIds) WITH ([value] int '$') AS [f] - ) + WHERE [s].[CustomerID] IS NOT NULL AND [s].[CustomerID] = [o0].[CustomerID] AND [o0].[OrderID] IN (@filteredOrderIds1, @filteredOrderIds2, @filteredOrderIds3) ) AS [o1] ORDER BY [s].[CustomerID], [o1].[OrderID] """); @@ -2170,7 +2169,9 @@ public override async Task Correlated_collection_after_distinct_with_complex_pro AssertSql( """ -@filteredOrderIds='[10248,10249,10250]' (Size = 4000) +@filteredOrderIds1='10248' +@filteredOrderIds2='10249' +@filteredOrderIds3='10250' SELECT [o0].[OrderID], [o0].[Complex], [o2].[Outer], [o2].[Inner], [o2].[OrderDate] FROM ( @@ -2180,10 +2181,7 @@ FROM [Orders] AS [o] OUTER APPLY ( SELECT [o0].[OrderID] AS [Outer], [o1].[OrderID] AS [Inner], [o1].[OrderDate] FROM [Orders] AS [o1] - WHERE [o1].[OrderID] = [o0].[OrderID] AND [o1].[OrderID] IN ( - SELECT [f].[value] - FROM OPENJSON(@filteredOrderIds) WITH ([value] int '$') AS [f] - ) + WHERE [o1].[OrderID] = [o0].[OrderID] AND [o1].[OrderID] IN (@filteredOrderIds1, @filteredOrderIds2, @filteredOrderIds3) ) AS [o2] ORDER BY [o0].[OrderID] """); @@ -2195,7 +2193,9 @@ public override async Task Correlated_collection_after_distinct_not_containing_o AssertSql( """ -@filteredOrderIds='[10248,10249,10250]' (Size = 4000) +@filteredOrderIds1='10248' +@filteredOrderIds2='10249' +@filteredOrderIds3='10250' SELECT [o0].[OrderDate], [o0].[CustomerID], [o2].[Outer1], [o2].[Outer2], [o2].[Inner], [o2].[OrderDate] FROM ( @@ -2205,10 +2205,7 @@ FROM [Orders] AS [o] OUTER APPLY ( SELECT [o0].[OrderDate] AS [Outer1], [o0].[CustomerID] AS [Outer2], [o1].[OrderID] AS [Inner], [o1].[OrderDate] FROM [Orders] AS [o1] - WHERE ([o1].[CustomerID] = [o0].[CustomerID] OR ([o1].[CustomerID] IS NULL AND [o0].[CustomerID] IS NULL)) AND [o1].[OrderID] IN ( - SELECT [f].[value] - FROM OPENJSON(@filteredOrderIds) WITH ([value] int '$') AS [f] - ) + WHERE ([o1].[CustomerID] = [o0].[CustomerID] OR ([o1].[CustomerID] IS NULL AND [o0].[CustomerID] IS NULL)) AND [o1].[OrderID] IN (@filteredOrderIds1, @filteredOrderIds2, @filteredOrderIds3) ) AS [o2] ORDER BY [o0].[OrderDate], [o0].[CustomerID] """); @@ -2232,7 +2229,9 @@ public override async Task Correlated_collection_after_groupby_with_complex_proj AssertSql( """ -@filteredOrderIds='[10248,10249,10250]' (Size = 4000) +@filteredOrderIds1='10248' +@filteredOrderIds2='10249' +@filteredOrderIds3='10250' SELECT [o2].[OrderID], [o2].[Complex], [o3].[Outer], [o3].[Inner], [o3].[OrderDate] FROM ( @@ -2246,10 +2245,7 @@ FROM [Orders] AS [o] OUTER APPLY ( SELECT [o2].[OrderID] AS [Outer], [o1].[OrderID] AS [Inner], [o1].[OrderDate] FROM [Orders] AS [o1] - WHERE [o1].[OrderID] = [o2].[OrderID] AND [o1].[OrderID] IN ( - SELECT [f].[value] - FROM OPENJSON(@filteredOrderIds) WITH ([value] int '$') AS [f] - ) + WHERE [o1].[OrderID] = [o2].[OrderID] AND [o1].[OrderID] IN (@filteredOrderIds1, @filteredOrderIds2, @filteredOrderIds3) ) AS [o3] ORDER BY [o2].[OrderID] """); @@ -2672,7 +2668,9 @@ public override async Task Correlated_collection_after_groupby_with_complex_proj AssertSql( """ -@filteredOrderIds='[10248,10249,10250]' (Size = 4000) +@filteredOrderIds1='10248' +@filteredOrderIds2='10249' +@filteredOrderIds3='10250' SELECT [o2].[CustomerID], [o2].[Complex], [o3].[Outer], [o3].[Inner], [o3].[OrderDate] FROM ( @@ -2686,10 +2684,7 @@ FROM [Orders] AS [o] OUTER APPLY ( SELECT [o2].[CustomerID] AS [Outer], [o1].[OrderID] AS [Inner], [o1].[OrderDate] FROM [Orders] AS [o1] - WHERE ([o1].[CustomerID] = [o2].[CustomerID] OR ([o1].[CustomerID] IS NULL AND [o2].[CustomerID] IS NULL)) AND [o1].[OrderID] IN ( - SELECT [f].[value] - FROM OPENJSON(@filteredOrderIds) WITH ([value] int '$') AS [f] - ) + WHERE ([o1].[CustomerID] = [o2].[CustomerID] OR ([o1].[CustomerID] IS NULL AND [o2].[CustomerID] IS NULL)) AND [o1].[OrderID] IN (@filteredOrderIds1, @filteredOrderIds2, @filteredOrderIds3) ) AS [o3] ORDER BY [o2].[CustomerID], [o2].[Complex] """); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindSplitIncludeNoTrackingQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindSplitIncludeNoTrackingQuerySqlServerTest.cs index 306ad8b0133..6453d868381 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindSplitIncludeNoTrackingQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindSplitIncludeNoTrackingQuerySqlServerTest.cs @@ -159,42 +159,33 @@ public override async Task Include_collection_OrderBy_list_does_not_contains(boo AssertSql( """ -@list='["ALFKI"]' (Size = 4000) +@list1='ALFKI' (Size = 5) (DbType = StringFixedLength) @p='1' SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] WHERE [c].[CustomerID] LIKE N'A%' ORDER BY CASE - WHEN [c].[CustomerID] NOT IN ( - SELECT [l].[value] - FROM OPENJSON(@list) WITH ([value] nchar(5) '$') AS [l] - ) THEN CAST(1 AS bit) + WHEN [c].[CustomerID] <> @list1 THEN CAST(1 AS bit) ELSE CAST(0 AS bit) END, [c].[CustomerID] OFFSET @p ROWS """, // """ -@list='["ALFKI"]' (Size = 4000) +@list2='ALFKI' (Size = 5) (DbType = StringFixedLength) @p='1' SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], [c0].[CustomerID] FROM ( SELECT [c].[CustomerID], CASE - WHEN [c].[CustomerID] NOT IN ( - SELECT [l].[value] - FROM OPENJSON(@list) WITH ([value] nchar(5) '$') AS [l] - ) THEN CAST(1 AS bit) + WHEN [c].[CustomerID] <> @list2 THEN CAST(1 AS bit) ELSE CAST(0 AS bit) END AS [c] FROM [Customers] AS [c] WHERE [c].[CustomerID] LIKE N'A%' ORDER BY CASE - WHEN [c].[CustomerID] NOT IN ( - SELECT [l].[value] - FROM OPENJSON(@list) WITH ([value] nchar(5) '$') AS [l] - ) THEN CAST(1 AS bit) + WHEN [c].[CustomerID] <> @list2 THEN CAST(1 AS bit) ELSE CAST(0 AS bit) END, [c].[CustomerID] OFFSET @p ROWS @@ -1157,42 +1148,33 @@ public override async Task Include_collection_OrderBy_list_contains(bool async) AssertSql( """ -@list='["ALFKI"]' (Size = 4000) +@list1='ALFKI' (Size = 5) (DbType = StringFixedLength) @p='1' SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] WHERE [c].[CustomerID] LIKE N'A%' ORDER BY CASE - WHEN [c].[CustomerID] IN ( - SELECT [l].[value] - FROM OPENJSON(@list) WITH ([value] nchar(5) '$') AS [l] - ) THEN CAST(1 AS bit) + WHEN [c].[CustomerID] = @list1 THEN CAST(1 AS bit) ELSE CAST(0 AS bit) END, [c].[CustomerID] OFFSET @p ROWS """, // """ -@list='["ALFKI"]' (Size = 4000) +@list2='ALFKI' (Size = 5) (DbType = StringFixedLength) @p='1' SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], [c0].[CustomerID] FROM ( SELECT [c].[CustomerID], CASE - WHEN [c].[CustomerID] IN ( - SELECT [l].[value] - FROM OPENJSON(@list) WITH ([value] nchar(5) '$') AS [l] - ) THEN CAST(1 AS bit) + WHEN [c].[CustomerID] = @list2 THEN CAST(1 AS bit) ELSE CAST(0 AS bit) END AS [c] FROM [Customers] AS [c] WHERE [c].[CustomerID] LIKE N'A%' ORDER BY CASE - WHEN [c].[CustomerID] IN ( - SELECT [l].[value] - FROM OPENJSON(@list) WITH ([value] nchar(5) '$') AS [l] - ) THEN CAST(1 AS bit) + WHEN [c].[CustomerID] = @list2 THEN CAST(1 AS bit) ELSE CAST(0 AS bit) END, [c].[CustomerID] OFFSET @p ROWS @@ -1605,44 +1587,24 @@ public override async Task Include_collection_OrderBy_empty_list_contains(bool a AssertSql( """ -@list='[]' (Size = 4000) @p='1' SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] WHERE [c].[CustomerID] LIKE N'A%' -ORDER BY CASE - WHEN [c].[CustomerID] IN ( - SELECT [l].[value] - FROM OPENJSON(@list) WITH ([value] nchar(5) '$') AS [l] - ) THEN CAST(1 AS bit) - ELSE CAST(0 AS bit) -END, [c].[CustomerID] +ORDER BY (SELECT 1), [c].[CustomerID] OFFSET @p ROWS """, // """ -@list='[]' (Size = 4000) @p='1' SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], [c0].[CustomerID] FROM ( - SELECT [c].[CustomerID], CASE - WHEN [c].[CustomerID] IN ( - SELECT [l].[value] - FROM OPENJSON(@list) WITH ([value] nchar(5) '$') AS [l] - ) THEN CAST(1 AS bit) - ELSE CAST(0 AS bit) - END AS [c] + SELECT [c].[CustomerID], CAST(0 AS bit) AS [c] FROM [Customers] AS [c] WHERE [c].[CustomerID] LIKE N'A%' - ORDER BY CASE - WHEN [c].[CustomerID] IN ( - SELECT [l].[value] - FROM OPENJSON(@list) WITH ([value] nchar(5) '$') AS [l] - ) THEN CAST(1 AS bit) - ELSE CAST(0 AS bit) - END, [c].[CustomerID] + ORDER BY (SELECT 1), [c].[CustomerID] OFFSET @p ROWS ) AS [c0] INNER JOIN [Orders] AS [o] ON [c0].[CustomerID] = [o].[CustomerID] @@ -1785,44 +1747,24 @@ public override async Task Include_collection_OrderBy_empty_list_does_not_contai AssertSql( """ -@list='[]' (Size = 4000) @p='1' SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] WHERE [c].[CustomerID] LIKE N'A%' -ORDER BY CASE - WHEN [c].[CustomerID] NOT IN ( - SELECT [l].[value] - FROM OPENJSON(@list) WITH ([value] nchar(5) '$') AS [l] - ) THEN CAST(1 AS bit) - ELSE CAST(0 AS bit) -END, [c].[CustomerID] +ORDER BY (SELECT 1), [c].[CustomerID] OFFSET @p ROWS """, // """ -@list='[]' (Size = 4000) @p='1' SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], [c0].[CustomerID] FROM ( - SELECT [c].[CustomerID], CASE - WHEN [c].[CustomerID] NOT IN ( - SELECT [l].[value] - FROM OPENJSON(@list) WITH ([value] nchar(5) '$') AS [l] - ) THEN CAST(1 AS bit) - ELSE CAST(0 AS bit) - END AS [c] + SELECT [c].[CustomerID], CAST(1 AS bit) AS [c] FROM [Customers] AS [c] WHERE [c].[CustomerID] LIKE N'A%' - ORDER BY CASE - WHEN [c].[CustomerID] NOT IN ( - SELECT [l].[value] - FROM OPENJSON(@list) WITH ([value] nchar(5) '$') AS [l] - ) THEN CAST(1 AS bit) - ELSE CAST(0 AS bit) - END, [c].[CustomerID] + ORDER BY (SELECT 1), [c].[CustomerID] OFFSET @p ROWS ) AS [c0] INNER JOIN [Orders] AS [o] ON [c0].[CustomerID] = [o].[CustomerID] diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindSplitIncludeQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindSplitIncludeQuerySqlServerTest.cs index 4f4a591a867..3a8541b00fc 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindSplitIncludeQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindSplitIncludeQuerySqlServerTest.cs @@ -2125,44 +2125,24 @@ public override async Task Include_collection_OrderBy_empty_list_contains(bool a AssertSql( """ -@list='[]' (Size = 4000) @p='1' SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] WHERE [c].[CustomerID] LIKE N'A%' -ORDER BY CASE - WHEN [c].[CustomerID] IN ( - SELECT [l].[value] - FROM OPENJSON(@list) WITH ([value] nchar(5) '$') AS [l] - ) THEN CAST(1 AS bit) - ELSE CAST(0 AS bit) -END, [c].[CustomerID] +ORDER BY (SELECT 1), [c].[CustomerID] OFFSET @p ROWS """, // """ -@list='[]' (Size = 4000) @p='1' SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], [c0].[CustomerID] FROM ( - SELECT [c].[CustomerID], CASE - WHEN [c].[CustomerID] IN ( - SELECT [l].[value] - FROM OPENJSON(@list) WITH ([value] nchar(5) '$') AS [l] - ) THEN CAST(1 AS bit) - ELSE CAST(0 AS bit) - END AS [c] + SELECT [c].[CustomerID], CAST(0 AS bit) AS [c] FROM [Customers] AS [c] WHERE [c].[CustomerID] LIKE N'A%' - ORDER BY CASE - WHEN [c].[CustomerID] IN ( - SELECT [l].[value] - FROM OPENJSON(@list) WITH ([value] nchar(5) '$') AS [l] - ) THEN CAST(1 AS bit) - ELSE CAST(0 AS bit) - END, [c].[CustomerID] + ORDER BY (SELECT 1), [c].[CustomerID] OFFSET @p ROWS ) AS [c0] INNER JOIN [Orders] AS [o] ON [c0].[CustomerID] = [o].[CustomerID] @@ -2176,44 +2156,24 @@ public override async Task Include_collection_OrderBy_empty_list_does_not_contai AssertSql( """ -@list='[]' (Size = 4000) @p='1' SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] WHERE [c].[CustomerID] LIKE N'A%' -ORDER BY CASE - WHEN [c].[CustomerID] NOT IN ( - SELECT [l].[value] - FROM OPENJSON(@list) WITH ([value] nchar(5) '$') AS [l] - ) THEN CAST(1 AS bit) - ELSE CAST(0 AS bit) -END, [c].[CustomerID] +ORDER BY (SELECT 1), [c].[CustomerID] OFFSET @p ROWS """, // """ -@list='[]' (Size = 4000) @p='1' SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], [c0].[CustomerID] FROM ( - SELECT [c].[CustomerID], CASE - WHEN [c].[CustomerID] NOT IN ( - SELECT [l].[value] - FROM OPENJSON(@list) WITH ([value] nchar(5) '$') AS [l] - ) THEN CAST(1 AS bit) - ELSE CAST(0 AS bit) - END AS [c] + SELECT [c].[CustomerID], CAST(1 AS bit) AS [c] FROM [Customers] AS [c] WHERE [c].[CustomerID] LIKE N'A%' - ORDER BY CASE - WHEN [c].[CustomerID] NOT IN ( - SELECT [l].[value] - FROM OPENJSON(@list) WITH ([value] nchar(5) '$') AS [l] - ) THEN CAST(1 AS bit) - ELSE CAST(0 AS bit) - END, [c].[CustomerID] + ORDER BY (SELECT 1), [c].[CustomerID] OFFSET @p ROWS ) AS [c0] INNER JOIN [Orders] AS [o] ON [c0].[CustomerID] = [o].[CustomerID] @@ -2227,42 +2187,33 @@ public override async Task Include_collection_OrderBy_list_contains(bool async) AssertSql( """ -@list='["ALFKI"]' (Size = 4000) +@list1='ALFKI' (Size = 5) (DbType = StringFixedLength) @p='1' SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] WHERE [c].[CustomerID] LIKE N'A%' ORDER BY CASE - WHEN [c].[CustomerID] IN ( - SELECT [l].[value] - FROM OPENJSON(@list) WITH ([value] nchar(5) '$') AS [l] - ) THEN CAST(1 AS bit) + WHEN [c].[CustomerID] = @list1 THEN CAST(1 AS bit) ELSE CAST(0 AS bit) END, [c].[CustomerID] OFFSET @p ROWS """, // """ -@list='["ALFKI"]' (Size = 4000) +@list2='ALFKI' (Size = 5) (DbType = StringFixedLength) @p='1' SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], [c0].[CustomerID] FROM ( SELECT [c].[CustomerID], CASE - WHEN [c].[CustomerID] IN ( - SELECT [l].[value] - FROM OPENJSON(@list) WITH ([value] nchar(5) '$') AS [l] - ) THEN CAST(1 AS bit) + WHEN [c].[CustomerID] = @list2 THEN CAST(1 AS bit) ELSE CAST(0 AS bit) END AS [c] FROM [Customers] AS [c] WHERE [c].[CustomerID] LIKE N'A%' ORDER BY CASE - WHEN [c].[CustomerID] IN ( - SELECT [l].[value] - FROM OPENJSON(@list) WITH ([value] nchar(5) '$') AS [l] - ) THEN CAST(1 AS bit) + WHEN [c].[CustomerID] = @list2 THEN CAST(1 AS bit) ELSE CAST(0 AS bit) END, [c].[CustomerID] OFFSET @p ROWS @@ -2278,42 +2229,33 @@ public override async Task Include_collection_OrderBy_list_does_not_contains(boo AssertSql( """ -@list='["ALFKI"]' (Size = 4000) +@list1='ALFKI' (Size = 5) (DbType = StringFixedLength) @p='1' SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] WHERE [c].[CustomerID] LIKE N'A%' ORDER BY CASE - WHEN [c].[CustomerID] NOT IN ( - SELECT [l].[value] - FROM OPENJSON(@list) WITH ([value] nchar(5) '$') AS [l] - ) THEN CAST(1 AS bit) + WHEN [c].[CustomerID] <> @list1 THEN CAST(1 AS bit) ELSE CAST(0 AS bit) END, [c].[CustomerID] OFFSET @p ROWS """, // """ -@list='["ALFKI"]' (Size = 4000) +@list2='ALFKI' (Size = 5) (DbType = StringFixedLength) @p='1' SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], [c0].[CustomerID] FROM ( SELECT [c].[CustomerID], CASE - WHEN [c].[CustomerID] NOT IN ( - SELECT [l].[value] - FROM OPENJSON(@list) WITH ([value] nchar(5) '$') AS [l] - ) THEN CAST(1 AS bit) + WHEN [c].[CustomerID] <> @list2 THEN CAST(1 AS bit) ELSE CAST(0 AS bit) END AS [c] FROM [Customers] AS [c] WHERE [c].[CustomerID] LIKE N'A%' ORDER BY CASE - WHEN [c].[CustomerID] NOT IN ( - SELECT [l].[value] - FROM OPENJSON(@list) WITH ([value] nchar(5) '$') AS [l] - ) THEN CAST(1 AS bit) + WHEN [c].[CustomerID] <> @list2 THEN CAST(1 AS bit) ELSE CAST(0 AS bit) END, [c].[CustomerID] OFFSET @p ROWS diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindStringIncludeQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindStringIncludeQuerySqlServerTest.cs index 3ca97ba898e..c6d7a2e914d 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindStringIncludeQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindStringIncludeQuerySqlServerTest.cs @@ -630,25 +630,19 @@ public override async Task Include_collection_OrderBy_list_does_not_contains(boo AssertSql( """ -@list='["ALFKI"]' (Size = 4000) +@list1='ALFKI' (Size = 5) (DbType = StringFixedLength) @p='1' SELECT [c0].[CustomerID], [c0].[Address], [c0].[City], [c0].[CompanyName], [c0].[ContactName], [c0].[ContactTitle], [c0].[Country], [c0].[Fax], [c0].[Phone], [c0].[PostalCode], [c0].[Region], [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] FROM ( SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], CASE - WHEN [c].[CustomerID] NOT IN ( - SELECT [l].[value] - FROM OPENJSON(@list) WITH ([value] nchar(5) '$') AS [l] - ) THEN CAST(1 AS bit) + WHEN [c].[CustomerID] <> @list1 THEN CAST(1 AS bit) ELSE CAST(0 AS bit) END AS [c] FROM [Customers] AS [c] WHERE [c].[CustomerID] LIKE N'A%' ORDER BY CASE - WHEN [c].[CustomerID] NOT IN ( - SELECT [l].[value] - FROM OPENJSON(@list) WITH ([value] nchar(5) '$') AS [l] - ) THEN CAST(1 AS bit) + WHEN [c].[CustomerID] <> @list1 THEN CAST(1 AS bit) ELSE CAST(0 AS bit) END OFFSET @p ROWS @@ -978,27 +972,14 @@ public override async Task Include_collection_OrderBy_empty_list_contains(bool a AssertSql( """ -@list='[]' (Size = 4000) @p='1' SELECT [c0].[CustomerID], [c0].[Address], [c0].[City], [c0].[CompanyName], [c0].[ContactName], [c0].[ContactTitle], [c0].[Country], [c0].[Fax], [c0].[Phone], [c0].[PostalCode], [c0].[Region], [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] FROM ( - SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], CASE - WHEN [c].[CustomerID] IN ( - SELECT [l].[value] - FROM OPENJSON(@list) WITH ([value] nchar(5) '$') AS [l] - ) THEN CAST(1 AS bit) - ELSE CAST(0 AS bit) - END AS [c] + SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], CAST(0 AS bit) AS [c] FROM [Customers] AS [c] WHERE [c].[CustomerID] LIKE N'A%' - ORDER BY CASE - WHEN [c].[CustomerID] IN ( - SELECT [l].[value] - FROM OPENJSON(@list) WITH ([value] nchar(5) '$') AS [l] - ) THEN CAST(1 AS bit) - ELSE CAST(0 AS bit) - END + ORDER BY (SELECT 1) OFFSET @p ROWS ) AS [c0] LEFT JOIN [Orders] AS [o] ON [c0].[CustomerID] = [o].[CustomerID] @@ -1374,25 +1355,19 @@ public override async Task Include_collection_OrderBy_list_contains(bool async) AssertSql( """ -@list='["ALFKI"]' (Size = 4000) +@list1='ALFKI' (Size = 5) (DbType = StringFixedLength) @p='1' SELECT [c0].[CustomerID], [c0].[Address], [c0].[City], [c0].[CompanyName], [c0].[ContactName], [c0].[ContactTitle], [c0].[Country], [c0].[Fax], [c0].[Phone], [c0].[PostalCode], [c0].[Region], [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] FROM ( SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], CASE - WHEN [c].[CustomerID] IN ( - SELECT [l].[value] - FROM OPENJSON(@list) WITH ([value] nchar(5) '$') AS [l] - ) THEN CAST(1 AS bit) + WHEN [c].[CustomerID] = @list1 THEN CAST(1 AS bit) ELSE CAST(0 AS bit) END AS [c] FROM [Customers] AS [c] WHERE [c].[CustomerID] LIKE N'A%' ORDER BY CASE - WHEN [c].[CustomerID] IN ( - SELECT [l].[value] - FROM OPENJSON(@list) WITH ([value] nchar(5) '$') AS [l] - ) THEN CAST(1 AS bit) + WHEN [c].[CustomerID] = @list1 THEN CAST(1 AS bit) ELSE CAST(0 AS bit) END OFFSET @p ROWS @@ -1853,27 +1828,14 @@ public override async Task Include_collection_OrderBy_empty_list_does_not_contai AssertSql( """ -@list='[]' (Size = 4000) @p='1' SELECT [c0].[CustomerID], [c0].[Address], [c0].[City], [c0].[CompanyName], [c0].[ContactName], [c0].[ContactTitle], [c0].[Country], [c0].[Fax], [c0].[Phone], [c0].[PostalCode], [c0].[Region], [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] FROM ( - SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], CASE - WHEN [c].[CustomerID] NOT IN ( - SELECT [l].[value] - FROM OPENJSON(@list) WITH ([value] nchar(5) '$') AS [l] - ) THEN CAST(1 AS bit) - ELSE CAST(0 AS bit) - END AS [c] + SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], CAST(1 AS bit) AS [c] FROM [Customers] AS [c] WHERE [c].[CustomerID] LIKE N'A%' - ORDER BY CASE - WHEN [c].[CustomerID] NOT IN ( - SELECT [l].[value] - FROM OPENJSON(@list) WITH ([value] nchar(5) '$') AS [l] - ) THEN CAST(1 AS bit) - ELSE CAST(0 AS bit) - END + ORDER BY (SELECT 1) OFFSET @p ROWS ) AS [c0] LEFT JOIN [Orders] AS [o] ON [c0].[CustomerID] = [o].[CustomerID] diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindWhereQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindWhereQuerySqlServerTest.cs index 483e31e1cc7..f0f1f20ff23 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindWhereQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindWhereQuerySqlServerTest.cs @@ -1464,10 +1464,17 @@ ORDER BY [c0].[CustomerID] """, // """ +@entity_equality_customer_Orders_OrderID1='10643' +@entity_equality_customer_Orders_OrderID2='10692' +@entity_equality_customer_Orders_OrderID3='10702' +@entity_equality_customer_Orders_OrderID4='10835' +@entity_equality_customer_Orders_OrderID5='10952' +@entity_equality_customer_Orders_OrderID6='11011' + SELECT [o].[OrderID], [o].[ProductID], [o].[Discount], [o].[Quantity], [o].[UnitPrice] FROM [Order Details] AS [o] INNER JOIN [Orders] AS [o0] ON [o].[OrderID] = [o0].[OrderID] -WHERE [o0].[OrderID] IN (10643, 10692, 10702, 10835, 10952, 11011) +WHERE [o0].[OrderID] IN (@entity_equality_customer_Orders_OrderID1, @entity_equality_customer_Orders_OrderID2, @entity_equality_customer_Orders_OrderID3, @entity_equality_customer_Orders_OrderID4, @entity_equality_customer_Orders_OrderID5, @entity_equality_customer_Orders_OrderID6) """); } @@ -1655,14 +1662,11 @@ public override async Task Generic_Ilist_contains_translates_to_server(bool asyn AssertSql( """ -@cities='["Seattle"]' (Size = 4000) +@cities1='Seattle' (Size = 15) SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[City] IN ( - SELECT [c0].[value] - FROM OPENJSON(@cities) WITH ([value] nvarchar(15) '$') AS [c0] -) +WHERE [c].[City] = @cities1 """); } @@ -2016,14 +2020,12 @@ public override async Task Where_list_object_contains_over_value_type(bool async AssertSql( """ -@orderIds='[10248,10249]' (Size = 4000) +@orderIds1='10248' +@orderIds2='10249' SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] FROM [Orders] AS [o] -WHERE [o].[OrderID] IN ( - SELECT [o0].[value] - FROM OPENJSON(@orderIds) WITH ([value] int '$') AS [o0] -) +WHERE [o].[OrderID] IN (@orderIds1, @orderIds2) """); } @@ -2033,14 +2035,12 @@ public override async Task Where_array_of_object_contains_over_value_type(bool a AssertSql( """ -@orderIds='[10248,10249]' (Size = 4000) +@orderIds1='10248' +@orderIds2='10249' SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] FROM [Orders] AS [o] -WHERE [o].[OrderID] IN ( - SELECT [o0].[value] - FROM OPENJSON(@orderIds) WITH ([value] int '$') AS [o0] -) +WHERE [o].[OrderID] IN (@orderIds1, @orderIds2) """); } @@ -2120,7 +2120,6 @@ public override async Task Multiple_AndAlso_on_same_column_converted_to_in_using { await base.Multiple_AndAlso_on_same_column_converted_to_in_using_parameters(async); - // issue #21462 AssertSql( """ @prm1='ALFKI' (Size = 5) (DbType = StringFixedLength) @@ -2137,7 +2136,6 @@ public override async Task Array_of_parameters_Contains_OrElse_comparison_with_c { await base.Array_of_parameters_Contains_OrElse_comparison_with_constant_gets_combined_to_one_in(async); - // issue #21462 AssertSql( """ @prm1='ALFKI' (Size = 5) (DbType = StringFixedLength) @@ -2153,7 +2151,6 @@ public override async Task Multiple_OrElse_on_same_column_with_null_parameter_co { await base.Multiple_OrElse_on_same_column_with_null_parameter_comparison_converted_to_in(async); - // issue #21462 AssertSql( """ SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] @@ -2168,14 +2165,12 @@ public override async Task Parameter_array_Contains_OrElse_comparison_with_const AssertSql( """ -@array='["ALFKI","ANATR"]' (Size = 4000) +@array1='ALFKI' (Size = 5) (DbType = StringFixedLength) +@array2='ANATR' (Size = 5) (DbType = StringFixedLength) SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[CustomerID] IN ( - SELECT [a].[value] - FROM OPENJSON(@array) WITH ([value] nchar(5) '$') AS [a] -) OR [c].[CustomerID] = N'ANTON' +WHERE [c].[CustomerID] IN (@array1, @array2) OR [c].[CustomerID] = N'ANTON' """); } @@ -2186,15 +2181,13 @@ public override async Task Parameter_array_Contains_OrElse_comparison_with_param AssertSql( """ @prm1='ANTON' (Size = 5) (DbType = StringFixedLength) -@array='["ALFKI","ANATR"]' (Size = 4000) +@array1='ALFKI' (Size = 5) (DbType = StringFixedLength) +@array2='ANATR' (Size = 5) (DbType = StringFixedLength) @prm2='ALFKI' (Size = 5) (DbType = StringFixedLength) SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[CustomerID] = @prm1 OR [c].[CustomerID] IN ( - SELECT [a].[value] - FROM OPENJSON(@array) WITH ([value] nchar(5) '$') AS [a] -) OR [c].[CustomerID] = @prm2 +WHERE [c].[CustomerID] = @prm1 OR [c].[CustomerID] IN (@array1, @array2) OR [c].[CustomerID] = @prm2 """); } @@ -2496,14 +2489,13 @@ public override async Task Where_Contains_and_comparison(bool async) AssertSql( """ -@customerIds='["ALFKI","FISSA","WHITC"]' (Size = 4000) +@customerIds1='ALFKI' (Size = 5) (DbType = StringFixedLength) +@customerIds2='FISSA' (Size = 5) (DbType = StringFixedLength) +@customerIds3='WHITC' (Size = 5) (DbType = StringFixedLength) SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[CustomerID] IN ( - SELECT [c0].[value] - FROM OPENJSON(@customerIds) WITH ([value] nchar(5) '$') AS [c0] -) AND [c].[City] = N'Seattle' +WHERE [c].[CustomerID] IN (@customerIds1, @customerIds2, @customerIds3) AND [c].[City] = N'Seattle' """); } @@ -2513,14 +2505,12 @@ public override async Task Where_Contains_or_comparison(bool async) AssertSql( """ -@customerIds='["ALFKI","FISSA"]' (Size = 4000) +@customerIds1='ALFKI' (Size = 5) (DbType = StringFixedLength) +@customerIds2='FISSA' (Size = 5) (DbType = StringFixedLength) SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[CustomerID] IN ( - SELECT [c0].[value] - FROM OPENJSON(@customerIds) WITH ([value] nchar(5) '$') AS [c0] -) OR [c].[City] = N'Seattle' +WHERE [c].[CustomerID] IN (@customerIds1, @customerIds2) OR [c].[City] = N'Seattle' """); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NullSemanticsQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NullSemanticsQuerySqlServerTest.cs index aeba22da99b..4885378191a 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/NullSemanticsQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/NullSemanticsQuerySqlServerTest.cs @@ -2730,14 +2730,11 @@ public override async Task Contains_with_local_array_closure_with_null(bool asyn AssertSql( """ -@ids_without_nulls='["Foo"]' (Size = 4000) +@ids1='Foo' (Size = 4000) SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE [e].[NullableStringA] IN ( - SELECT [i].[value] - FROM OPENJSON(@ids_without_nulls) AS [i] -) OR [e].[NullableStringA] IS NULL +WHERE [e].[NullableStringA] IS NULL OR [e].[NullableStringA] = @ids1 """); } @@ -2747,14 +2744,11 @@ public override async Task Contains_with_local_array_closure_false_with_null(boo AssertSql( """ -@ids_without_nulls='["Foo"]' (Size = 4000) +@ids1='Foo' (Size = 4000) SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE [e].[NullableStringA] NOT IN ( - SELECT [i].[value] - FROM OPENJSON(@ids_without_nulls) AS [i] -) AND [e].[NullableStringA] IS NOT NULL +WHERE [e].[NullableStringA] IS NOT NULL AND [e].[NullableStringA] <> @ids1 """); } @@ -2764,14 +2758,11 @@ public override async Task Contains_with_local_nullable_array_closure_negated(bo AssertSql( """ -@ids='["Foo"]' (Size = 4000) +@ids1='Foo' (Size = 4000) SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE [e].[NullableStringA] NOT IN ( - SELECT [i].[value] - FROM OPENJSON(@ids) WITH ([value] nvarchar(max) '$') AS [i] -) OR [e].[NullableStringA] IS NULL +WHERE [e].[NullableStringA] <> @ids1 OR [e].[NullableStringA] IS NULL """); } @@ -2781,14 +2772,11 @@ public override async Task Contains_with_local_array_closure_with_multiple_nulls AssertSql( """ -@ids_without_nulls='["Foo"]' (Size = 4000) +@ids2='Foo' (Size = 4000) SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE [e].[NullableStringA] IN ( - SELECT [i].[value] - FROM OPENJSON(@ids_without_nulls) AS [i] -) OR [e].[NullableStringA] IS NULL +WHERE [e].[NullableStringA] IS NULL OR [e].[NullableStringA] = @ids2 """); } @@ -3089,14 +3077,12 @@ public override async Task Where_conditional_search_condition_in_result(bool asy AssertSql( """ -@list='["Foo","Bar"]' (Size = 4000) +@list1='Foo' (Size = 4000) +@list2='Bar' (Size = 4000) SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE [e].[StringA] IN ( - SELECT [l].[value] - FROM OPENJSON(@list) WITH ([value] nvarchar(max) '$') AS [l] -) +WHERE [e].[StringA] IN (@list1, @list2) """, // """ @@ -3135,14 +3121,12 @@ public override void Where_contains_on_parameter_array_with_relational_null_sema AssertSql( """ -@names='["Foo","Bar"]' (Size = 4000) +@names1='Foo' (Size = 4000) +@names2='Bar' (Size = 4000) SELECT [e].[NullableStringA] FROM [Entities1] AS [e] -WHERE [e].[NullableStringA] IN ( - SELECT [n].[value] - FROM OPENJSON(@names) WITH ([value] nvarchar(max) '$') AS [n] -) +WHERE [e].[NullableStringA] IN (@names1, @names2) """); } @@ -3152,14 +3136,9 @@ public override void Where_contains_on_parameter_empty_array_with_relational_nul AssertSql( """ -@names='[]' (Size = 4000) - SELECT [e].[NullableStringA] FROM [Entities1] AS [e] -WHERE [e].[NullableStringA] IN ( - SELECT [n].[value] - FROM OPENJSON(@names) WITH ([value] nvarchar(max) '$') AS [n] -) +WHERE 0 = 1 """); } @@ -3169,14 +3148,11 @@ public override void Where_contains_on_parameter_array_with_just_null_with_relat AssertSql( """ -@names='[null]' (Size = 4000) +@names1=NULL (Size = 4000) SELECT [e].[NullableStringA] FROM [Entities1] AS [e] -WHERE [e].[NullableStringA] IN ( - SELECT [n].[value] - FROM OPENJSON(@names) WITH ([value] nvarchar(max) '$') AS [n] -) +WHERE [e].[NullableStringA] = @names1 """); } @@ -3643,47 +3619,39 @@ public override async Task Null_semantics_contains(bool async) AssertSql( """ -@ids='[1,2]' (Size = 4000) +@ids1='1' +@ids2='2' SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE [e].[NullableIntA] IN ( - SELECT [i].[value] - FROM OPENJSON(@ids) WITH ([value] int '$') AS [i] -) +WHERE [e].[NullableIntA] IN (@ids1, @ids2) """, // """ -@ids='[1,2]' (Size = 4000) +@ids1='1' +@ids2='2' SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE [e].[NullableIntA] NOT IN ( - SELECT [i].[value] - FROM OPENJSON(@ids) WITH ([value] int '$') AS [i] -) OR [e].[NullableIntA] IS NULL +WHERE [e].[NullableIntA] NOT IN (@ids1, @ids2) OR [e].[NullableIntA] IS NULL """, // """ -@ids2_without_nulls='[1,2]' (Size = 4000) +@ids21='1' +@ids22='2' SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE [e].[NullableIntA] IN ( - SELECT [i].[value] - FROM OPENJSON(@ids2_without_nulls) AS [i] -) OR [e].[NullableIntA] IS NULL +WHERE [e].[NullableIntA] IN (@ids21, @ids22) OR [e].[NullableIntA] IS NULL """, // """ -@ids2_without_nulls='[1,2]' (Size = 4000) +@ids21='1' +@ids22='2' SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE [e].[NullableIntA] NOT IN ( - SELECT [i].[value] - FROM OPENJSON(@ids2_without_nulls) AS [i] -) AND [e].[NullableIntA] IS NOT NULL +WHERE [e].[NullableIntA] NOT IN (@ids21, @ids22) AND [e].[NullableIntA] IS NOT NULL """, // """ @@ -3717,47 +3685,26 @@ public override async Task Null_semantics_contains_array_with_no_values(bool asy AssertSql( """ -@ids='[]' (Size = 4000) - SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE [e].[NullableIntA] IN ( - SELECT [i].[value] - FROM OPENJSON(@ids) WITH ([value] int '$') AS [i] -) +WHERE 0 = 1 """, // """ -@ids='[]' (Size = 4000) - SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE [e].[NullableIntA] NOT IN ( - SELECT [i].[value] - FROM OPENJSON(@ids) WITH ([value] int '$') AS [i] -) OR [e].[NullableIntA] IS NULL """, // """ -@ids2_without_nulls='[]' (Size = 4000) - SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE [e].[NullableIntA] IN ( - SELECT [i].[value] - FROM OPENJSON(@ids2_without_nulls) AS [i] -) OR [e].[NullableIntA] IS NULL +WHERE [e].[NullableIntA] IS NULL """, // """ -@ids2_without_nulls='[]' (Size = 4000) - SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE [e].[NullableIntA] NOT IN ( - SELECT [i].[value] - FROM OPENJSON(@ids2_without_nulls) AS [i] -) AND [e].[NullableIntA] IS NOT NULL +WHERE [e].[NullableIntA] IS NOT NULL """, // """ @@ -4117,91 +4064,61 @@ public override async Task Null_semantics_contains_non_nullable_item_with_values AssertSql( """ -@ids='[1,2,null]' (Size = 4000) +@ids1='1' +@ids2='2' SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE [e].[IntA] IN ( - SELECT [i].[value] - FROM OPENJSON(@ids) WITH ([value] int '$') AS [i] -) +WHERE [e].[IntA] IN (@ids1, @ids2) """, // """ -@ids_without_nulls='[1,2]' (Size = 4000) +@ids1='1' +@ids2='2' SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE [e].[IntA] NOT IN ( - SELECT [i].[value] - FROM OPENJSON(@ids_without_nulls) AS [i] -) +WHERE [e].[IntA] NOT IN (@ids1, @ids2) """, // """ -@ids2='[1,2]' (Size = 4000) +@ids21='1' +@ids22='2' SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE [e].[IntA] IN ( - SELECT [i].[value] - FROM OPENJSON(@ids2) WITH ([value] int '$') AS [i] -) +WHERE [e].[IntA] IN (@ids21, @ids22) """, // """ -@ids2='[1,2]' (Size = 4000) +@ids21='1' +@ids22='2' SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE [e].[IntA] NOT IN ( - SELECT [i].[value] - FROM OPENJSON(@ids2) WITH ([value] int '$') AS [i] -) +WHERE [e].[IntA] NOT IN (@ids21, @ids22) """, // """ -@ids3='[]' (Size = 4000) - SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE [e].[IntA] IN ( - SELECT [i].[value] - FROM OPENJSON(@ids3) WITH ([value] int '$') AS [i] -) +WHERE 0 = 1 """, // """ -@ids3='[]' (Size = 4000) - SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE [e].[IntA] NOT IN ( - SELECT [i].[value] - FROM OPENJSON(@ids3) WITH ([value] int '$') AS [i] -) """, // """ -@ids4='[null]' (Size = 4000) - SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE [e].[IntA] IN ( - SELECT [i].[value] - FROM OPENJSON(@ids4) WITH ([value] int '$') AS [i] -) +WHERE 0 = 1 """, // """ -@ids4_without_nulls='[]' (Size = 4000) - SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE [e].[IntA] NOT IN ( - SELECT [i].[value] - FROM OPENJSON(@ids4_without_nulls) AS [i] -) """); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/PrecompiledQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/PrecompiledQuerySqlServerTest.cs index b06bd80a435..328577fc1b9 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/PrecompiledQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/PrecompiledQuerySqlServerTest.cs @@ -1800,14 +1800,13 @@ public override async Task Contains_with_parameterized_collection() AssertSql( """ -@ids='[1,2,3]' (Size = 4000) +@ids1='1' +@ids2='2' +@ids3='3' SELECT [b].[Id], [b].[Name], [b].[Json] FROM [Blogs] AS [b] -WHERE [b].[Id] IN ( - SELECT [i].[value] - FROM OPENJSON(@ids) WITH ([value] int '$') AS [i] -) +WHERE [b].[Id] IN (@ids1, @ids2, @ids3) """); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/PrecompiledSqlPregenerationQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/PrecompiledSqlPregenerationQuerySqlServerTest.cs index 2d8990fd9dd..672449555e3 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/PrecompiledSqlPregenerationQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/PrecompiledSqlPregenerationQuerySqlServerTest.cs @@ -239,9 +239,12 @@ await Test( AssertSql( """ +@names1='foo' (Size = 4000) +@names2='bar' (Size = 4000) + SELECT [b].[Id], [b].[Name] FROM [Blogs] AS [b] -WHERE [b].[Name] IN (N'foo', N'bar') +WHERE [b].[Name] IN (@names1, @names2) """); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQueryOldSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQueryOldSqlServerTest.cs index d99ee606b20..d288947ccfd 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQueryOldSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQueryOldSqlServerTest.cs @@ -16,6 +16,8 @@ namespace Microsoft.EntityFrameworkCore.Query; public class PrimitiveCollectionsQueryOldSqlServerTest : PrimitiveCollectionsQueryRelationalTestBase< PrimitiveCollectionsQueryOldSqlServerTest.PrimitiveCollectionsQueryOldSqlServerFixture> { + public override int? NumberOfValuesForHugeParameterCollectionTests { get; } = 5000; + public PrimitiveCollectionsQueryOldSqlServerTest( PrimitiveCollectionsQueryOldSqlServerFixture fixture, ITestOutputHelper testOutputHelper) @@ -459,8 +461,23 @@ public override Task Inline_collection_Contains_with_EF_Parameter(bool async) public override Task Inline_collection_Count_with_column_predicate_with_EF_Parameter(bool async) => AssertCompatibilityLevelTooLow(() => base.Inline_collection_Count_with_column_predicate_with_EF_Parameter(async)); - public override Task Parameter_collection_Count(bool async) - => AssertCompatibilityLevelTooLow(() => base.Parameter_collection_Count(async)); + public override async Task Parameter_collection_Count(bool async) + { + await base.Parameter_collection_Count(async); + + AssertSql( + """ +@ids1='2' +@ids2='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].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE ( + SELECT COUNT(*) + FROM (VALUES (@ids1), (@ids2)) AS [i]([Value]) + WHERE [i].[Value] > [p].[Id]) = 1 +"""); + } public override async Task Parameter_collection_of_ints_Contains_int(bool async) { @@ -468,15 +485,21 @@ public override async Task Parameter_collection_of_ints_Contains_int(bool async) AssertSql( """ +@ints1='10' +@ints2='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].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[Int] IN (10, 999) +WHERE [p].[Int] IN (@ints1, @ints2) """, // """ +@ints1='10' +@ints2='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].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[Int] NOT IN (10, 999) +WHERE [p].[Int] NOT IN (@ints1, @ints2) """); } @@ -486,15 +509,21 @@ public override async Task Parameter_collection_HashSet_of_ints_Contains_int(boo AssertSql( """ +@ints1='10' +@ints2='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].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[Int] IN (10, 999) +WHERE [p].[Int] IN (@ints1, @ints2) """, // """ +@ints1='10' +@ints2='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].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[Int] NOT IN (10, 999) +WHERE [p].[Int] NOT IN (@ints1, @ints2) """); } @@ -504,15 +533,21 @@ public override async Task Parameter_collection_ImmutableArray_of_ints_Contains_ AssertSql( """ +@ints1='10' +@ints2='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].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[Int] IN (10, 999) +WHERE [p].[Int] IN (@ints1, @ints2) """, // """ +@ints1='10' +@ints2='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].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[Int] NOT IN (10, 999) +WHERE [p].[Int] NOT IN (@ints1, @ints2) """); } @@ -522,15 +557,21 @@ public override async Task Parameter_collection_of_ints_Contains_nullable_int(bo AssertSql( """ +@ints1='10' +@ints2='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].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[NullableInt] IN (10, 999) +WHERE [p].[NullableInt] IN (@ints1, @ints2) """, // """ +@ints1='10' +@ints2='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].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[NullableInt] NOT IN (10, 999) OR [p].[NullableInt] IS NULL +WHERE [p].[NullableInt] NOT IN (@ints1, @ints2) OR [p].[NullableInt] IS NULL """); } @@ -540,15 +581,21 @@ public override async Task Parameter_collection_of_nullable_ints_Contains_int(bo AssertSql( """ +@nullableInts1='10' +@nullableInts2='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].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[Int] IN (10, 999) +WHERE [p].[Int] IN (@nullableInts1, @nullableInts2) """, // """ +@nullableInts1='10' +@nullableInts2='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].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[Int] NOT IN (10, 999) +WHERE [p].[Int] NOT IN (@nullableInts1, @nullableInts2) """); } @@ -558,15 +605,19 @@ public override async Task Parameter_collection_of_nullable_ints_Contains_nullab AssertSql( """ +@nullableInts2='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].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[NullableInt] IS NULL OR [p].[NullableInt] = 999 +WHERE [p].[NullableInt] IS NULL OR [p].[NullableInt] = @nullableInts2 """, // """ +@nullableInts2='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].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[NullableInt] IS NOT NULL AND [p].[NullableInt] <> 999 +WHERE [p].[NullableInt] IS NOT NULL AND [p].[NullableInt] <> @nullableInts2 """); } @@ -576,15 +627,21 @@ public override async Task Parameter_collection_of_strings_Contains_string(bool AssertSql( """ +@strings1='10' (Size = 4000) +@strings2='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].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[String] IN (N'10', N'999') +WHERE [p].[String] IN (@strings1, @strings2) """, // """ +@strings1='10' (Size = 4000) +@strings2='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].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[String] NOT IN (N'10', N'999') +WHERE [p].[String] NOT IN (@strings1, @strings2) """); } @@ -594,15 +651,21 @@ public override async Task Parameter_collection_of_strings_Contains_nullable_str AssertSql( """ +@strings1='10' (Size = 4000) +@strings2='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].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[NullableString] IN (N'10', N'999') +WHERE [p].[NullableString] IN (@strings1, @strings2) """, // """ +@strings1='10' (Size = 4000) +@strings2='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].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[NullableString] NOT IN (N'10', N'999') OR [p].[NullableString] IS NULL +WHERE [p].[NullableString] NOT IN (@strings1, @strings2) OR [p].[NullableString] IS NULL """); } @@ -612,15 +675,19 @@ public override async Task Parameter_collection_of_nullable_strings_Contains_str AssertSql( """ +@strings1='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].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[String] = N'10' +WHERE [p].[String] = @strings1 """, // """ +@strings1='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].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[String] <> N'10' +WHERE [p].[String] <> @strings1 """); } @@ -630,15 +697,19 @@ public override async Task Parameter_collection_of_nullable_strings_Contains_nul AssertSql( """ +@strings1='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].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[NullableString] IS NULL OR [p].[NullableString] = N'999' +WHERE [p].[NullableString] IS NULL OR [p].[NullableString] = @strings1 """, // """ +@strings1='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].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[NullableString] IS NOT NULL AND [p].[NullableString] <> N'999' +WHERE [p].[NullableString] IS NOT NULL AND [p].[NullableString] <> @strings1 """); } @@ -648,9 +719,12 @@ public override async Task Parameter_collection_of_DateTimes_Contains(bool async AssertSql( """ +@dateTimes1='2020-01-10T12:30:00.0000000Z' +@dateTimes2='9999-01-01T00:00:00.0000000Z' + 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[DateTime] IN ('2020-01-10T12:30:00.0000000Z', '9999-01-01T00:00:00.0000000Z') +WHERE [p].[DateTime] IN (@dateTimes1, @dateTimes2) """); } @@ -660,9 +734,11 @@ public override async Task Parameter_collection_of_bools_Contains(bool async) AssertSql( """ +@bools1='True' + 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[Bool] = CAST(1 AS bit) +WHERE [p].[Bool] = @bools1 """); } @@ -672,9 +748,12 @@ public override async Task Parameter_collection_of_enums_Contains(bool async) AssertSql( """ +@enums1='0' +@enums2='3' + 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[Enum] IN (0, 3) +WHERE [p].[Enum] IN (@enums1, @enums2) """); } @@ -732,6 +811,21 @@ SELECT COUNT(*) """); } + public override async Task Parameter_collection_Count_with_huge_number_of_values(bool async) + { + await base.Parameter_collection_Count_with_huge_number_of_values(async); + + Assert.Contains("VALUES", Fixture.TestSqlLoggerFactory.SqlStatements[0], StringComparison.Ordinal); + } + + public override async Task Parameter_collection_of_ints_Contains_int_with_huge_number_of_values(bool async) + { + await base.Parameter_collection_of_ints_Contains_int_with_huge_number_of_values(async); + + Assert.DoesNotContain("OPENJSON", Fixture.TestSqlLoggerFactory.SqlStatements[0], StringComparison.Ordinal); + Assert.DoesNotContain("OPENJSON", Fixture.TestSqlLoggerFactory.SqlStatements[1], StringComparison.Ordinal); + } + public override Task Column_collection_of_ints_Contains(bool async) => AssertCompatibilityLevelTooLow(() => base.Column_collection_of_ints_Contains(async)); @@ -839,11 +933,45 @@ ORDER BY [v].[_ord] """); } - public override Task Parameter_collection_index_Column_equal_Column(bool async) - => AssertCompatibilityLevelTooLow(() => base.Parameter_collection_index_Column_equal_Column(async)); + public override async Task Parameter_collection_index_Column_equal_Column(bool async) + { + await base.Parameter_collection_index_Column_equal_Column(async); - public override Task Parameter_collection_index_Column_equal_constant(bool async) - => AssertCompatibilityLevelTooLow(() => base.Parameter_collection_index_Column_equal_constant(async)); + AssertSql( + """ +@ints1='0' +@ints2='2' +@ints3='3' + +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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE ( + SELECT [i].[Value] + FROM (VALUES (1, @ints1), (2, @ints2), (3, @ints3)) AS [i]([_ord], [Value]) + ORDER BY [i].[_ord] + OFFSET [p].[Int] ROWS FETCH NEXT 1 ROWS ONLY) = [p].[Int] +"""); + } + + public override async Task Parameter_collection_index_Column_equal_constant(bool async) + { + await base.Parameter_collection_index_Column_equal_constant(async); + + AssertSql( + """ +@ints1='1' +@ints2='2' +@ints3='3' + +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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE ( + SELECT [i].[Value] + FROM (VALUES (1, @ints1), (2, @ints2), (3, @ints3)) AS [i]([_ord], [Value]) + ORDER BY [i].[_ord] + OFFSET [p].[Int] ROWS FETCH NEXT 1 ROWS ONLY) = 1 +"""); + } public override Task Column_collection_ElementAt(bool async) => AssertCompatibilityLevelTooLow(() => base.Column_collection_ElementAt(async)); @@ -999,16 +1127,38 @@ public override void Parameter_collection_in_subquery_and_Convert_as_compiled_qu // Base implementation asserts that a different exception is thrown } - public override Task Parameter_collection_in_subquery_Count_as_compiled_query(bool async) - => AssertTranslationFailed(() => base.Parameter_collection_in_subquery_Count_as_compiled_query(async)); + public override async Task Parameter_collection_in_subquery_Count_as_compiled_query(bool async) + { + await base.Parameter_collection_in_subquery_Count_as_compiled_query(async); + + AssertSql( + """ +@ints1='10' +@ints2='111' + +SELECT COUNT(*) +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE ( + SELECT COUNT(*) + FROM ( + SELECT [i].[Value] AS [Value0] + FROM (VALUES (1, @ints1), (2, @ints2)) AS [i]([_ord], [Value]) + ORDER BY [i].[_ord] + OFFSET 1 ROWS + ) AS [i0] + WHERE [i0].[Value0] > [p].[Id]) = 1 +"""); + } public override Task Column_collection_in_subquery_Union_parameter_collection(bool async) => AssertCompatibilityLevelTooLow(() => base.Column_collection_in_subquery_Union_parameter_collection(async)); - // Base implementation asserts that a different exception is thrown - public override Task Parameter_collection_in_subquery_Union_another_parameter_collection_as_compiled_query(bool async) - => Assert.ThrowsAsync( - () => base.Parameter_collection_in_subquery_Union_another_parameter_collection_as_compiled_query(async)); + public override async Task Parameter_collection_in_subquery_Union_another_parameter_collection_as_compiled_query(bool async) + { + await base.Parameter_collection_in_subquery_Union_another_parameter_collection_as_compiled_query(async); + + AssertSql(); + } public override async Task Project_collection_of_ints_simple(bool async) { @@ -1156,12 +1306,19 @@ public override async Task Nested_contains_with_Lists_and_no_inferred_type_mappi AssertSql( """ +@ints1='1' +@ints2='2' +@ints3='3' +@strings1='one' (Size = 4000) +@strings2='two' (Size = 4000) +@strings3='three' (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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] WHERE CASE - WHEN [p].[Int] IN (1, 2, 3) THEN N'one' + WHEN [p].[Int] IN (@ints1, @ints2, @ints3) THEN N'one' ELSE N'two' -END IN (N'one', N'two', N'three') +END IN (@strings1, @strings2, @strings3) """); } @@ -1171,12 +1328,19 @@ public override async Task Nested_contains_with_arrays_and_no_inferred_type_mapp AssertSql( """ +@ints1='1' +@ints2='2' +@ints3='3' +@strings1='one' (Size = 4000) +@strings2='two' (Size = 4000) +@strings3='three' (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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] WHERE CASE - WHEN [p].[Int] IN (1, 2, 3) THEN N'one' + WHEN [p].[Int] IN (@ints1, @ints2, @ints3) THEN N'one' ELSE N'two' -END IN (N'one', N'two', N'three') +END IN (@strings1, @strings2, @strings3) """); } @@ -1186,15 +1350,21 @@ public override async Task Parameter_collection_of_structs_Contains_struct(bool AssertSql( """ +@values1='22' +@values2='33' + 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[WrappedId] IN (22, 33) +WHERE [p].[WrappedId] IN (@values1, @values2) """, // """ +@values1='11' +@values2='44' + 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[WrappedId] NOT IN (11, 44) +WHERE [p].[WrappedId] NOT IN (@values1, @values2) """); } @@ -1204,15 +1374,21 @@ public override async Task Parameter_collection_of_structs_Contains_nullable_str AssertSql( """ +@values1='22' +@values2='33' + 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[NullableWrappedId] IN (22, 33) +WHERE [p].[NullableWrappedId] IN (@values1, @values2) """, // """ +@values1='11' +@values2='44' + 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[NullableWrappedId] NOT IN (11, 44) OR [p].[NullableWrappedId] IS NULL +WHERE [p].[NullableWrappedId] NOT IN (@values1, @values2) OR [p].[NullableWrappedId] IS NULL """); } @@ -1222,15 +1398,21 @@ public override async Task Parameter_collection_of_structs_Contains_nullable_str AssertSql( """ +@values1='22' +@values2='33' + 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[NullableWrappedIdWithNullableComparer] IN (22, 33) +WHERE [p].[NullableWrappedIdWithNullableComparer] IN (@values1, @values2) """, // """ +@values1='11' +@values2='44' + 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[NullableWrappedId] NOT IN (11, 44) OR [p].[NullableWrappedId] IS NULL +WHERE [p].[NullableWrappedId] NOT IN (@values1, @values2) OR [p].[NullableWrappedId] IS NULL """); } @@ -1240,15 +1422,20 @@ public override async Task Parameter_collection_of_nullable_structs_Contains_str AssertSql( """ +@values2='22' + 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[WrappedId] = 22 +WHERE [p].[WrappedId] = @values2 """, // """ +@values1='11' +@values2='44' + 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[WrappedId] NOT IN (11, 44) +WHERE [p].[WrappedId] NOT IN (@values1, @values2) """); } @@ -1258,15 +1445,20 @@ public override async Task Parameter_collection_of_nullable_structs_Contains_nul AssertSql( """ +@values2='22' + 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[NullableWrappedId] IS NULL OR [p].[NullableWrappedId] = 22 +WHERE [p].[NullableWrappedId] IS NULL OR [p].[NullableWrappedId] = @values2 """, // """ +@values1='11' +@values2='44' + 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[NullableWrappedId] NOT IN (11, 44) OR [p].[NullableWrappedId] IS NULL +WHERE [p].[NullableWrappedId] NOT IN (@values1, @values2) OR [p].[NullableWrappedId] IS NULL """); } @@ -1276,15 +1468,20 @@ public override async Task Parameter_collection_of_nullable_structs_Contains_nul AssertSql( """ +@values2='22' + 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[NullableWrappedIdWithNullableComparer] IS NULL OR [p].[NullableWrappedIdWithNullableComparer] = 22 +WHERE [p].[NullableWrappedIdWithNullableComparer] IS NULL OR [p].[NullableWrappedIdWithNullableComparer] = @values2 """, // """ +@values1='11' +@values2='44' + 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[NullableWrappedIdWithNullableComparer] NOT IN (11, 44) OR [p].[NullableWrappedIdWithNullableComparer] IS NULL +WHERE [p].[NullableWrappedIdWithNullableComparer] NOT IN (@values1, @values2) OR [p].[NullableWrappedIdWithNullableComparer] IS NULL """); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQuerySqlServer160Test.cs b/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQuerySqlServer160Test.cs index 2596af4e190..c8066025ecf 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQuerySqlServer160Test.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQuerySqlServer160Test.cs @@ -7,6 +7,8 @@ namespace Microsoft.EntityFrameworkCore.Query; public class PrimitiveCollectionsQuerySqlServer160Test : PrimitiveCollectionsQueryRelationalTestBase< PrimitiveCollectionsQuerySqlServer160Test.PrimitiveCollectionsQuerySqlServerFixture> { + public override int? NumberOfValuesForHugeParameterCollectionTests { get; } = 5000; + public PrimitiveCollectionsQuerySqlServer160Test(PrimitiveCollectionsQuerySqlServerFixture fixture, ITestOutputHelper testOutputHelper) : base(fixture) { @@ -458,14 +460,15 @@ public override async Task Parameter_collection_Count(bool async) AssertSql( """ -@ids='[2,999]' (Size = 4000) +@ids1='2' +@ids2='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].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] WHERE ( SELECT COUNT(*) - FROM OPENJSON(@ids) WITH ([value] int '$') AS [i] - WHERE [i].[value] > [p].[Id]) = 1 + FROM (VALUES (@ids1), (@ids2)) AS [i]([Value]) + WHERE [i].[Value] > [p].[Id]) = 1 """); } @@ -475,25 +478,21 @@ public override async Task Parameter_collection_of_ints_Contains_int(bool async) AssertSql( """ -@ints='[10,999]' (Size = 4000) +@ints1='10' +@ints2='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].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[Int] IN ( - SELECT [i].[value] - FROM OPENJSON(@ints) WITH ([value] int '$') AS [i] -) +WHERE [p].[Int] IN (@ints1, @ints2) """, // """ -@ints='[10,999]' (Size = 4000) +@ints1='10' +@ints2='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].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[Int] NOT IN ( - SELECT [i].[value] - FROM OPENJSON(@ints) WITH ([value] int '$') AS [i] -) +WHERE [p].[Int] NOT IN (@ints1, @ints2) """); } @@ -503,25 +502,21 @@ public override async Task Parameter_collection_HashSet_of_ints_Contains_int(boo AssertSql( """ -@ints='[10,999]' (Size = 4000) +@ints1='10' +@ints2='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].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[Int] IN ( - SELECT [i].[value] - FROM OPENJSON(@ints) WITH ([value] int '$') AS [i] -) +WHERE [p].[Int] IN (@ints1, @ints2) """, // """ -@ints='[10,999]' (Size = 4000) +@ints1='10' +@ints2='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].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[Int] NOT IN ( - SELECT [i].[value] - FROM OPENJSON(@ints) WITH ([value] int '$') AS [i] -) +WHERE [p].[Int] NOT IN (@ints1, @ints2) """); } @@ -531,25 +526,21 @@ public override async Task Parameter_collection_ImmutableArray_of_ints_Contains_ AssertSql( """ -@ints='[10,999]' (Nullable = false) (Size = 4000) +@ints1='10' +@ints2='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].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[Int] IN ( - SELECT [i].[value] - FROM OPENJSON(@ints) WITH ([value] int '$') AS [i] -) +WHERE [p].[Int] IN (@ints1, @ints2) """, // """ -@ints='[10,999]' (Nullable = false) (Size = 4000) +@ints1='10' +@ints2='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].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[Int] NOT IN ( - SELECT [i].[value] - FROM OPENJSON(@ints) WITH ([value] int '$') AS [i] -) +WHERE [p].[Int] NOT IN (@ints1, @ints2) """); } @@ -559,25 +550,21 @@ public override async Task Parameter_collection_of_ints_Contains_nullable_int(bo AssertSql( """ -@ints='[10,999]' (Size = 4000) +@ints1='10' +@ints2='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].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[NullableInt] IN ( - SELECT [i].[value] - FROM OPENJSON(@ints) WITH ([value] int '$') AS [i] -) +WHERE [p].[NullableInt] IN (@ints1, @ints2) """, // """ -@ints='[10,999]' (Size = 4000) +@ints1='10' +@ints2='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].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[NullableInt] NOT IN ( - SELECT [i].[value] - FROM OPENJSON(@ints) WITH ([value] int '$') AS [i] -) OR [p].[NullableInt] IS NULL +WHERE [p].[NullableInt] NOT IN (@ints1, @ints2) OR [p].[NullableInt] IS NULL """); } @@ -587,25 +574,21 @@ public override async Task Parameter_collection_of_nullable_ints_Contains_int(bo AssertSql( """ -@nullableInts='[10,999]' (Size = 4000) +@nullableInts1='10' +@nullableInts2='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].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[Int] IN ( - SELECT [n].[value] - FROM OPENJSON(@nullableInts) WITH ([value] int '$') AS [n] -) +WHERE [p].[Int] IN (@nullableInts1, @nullableInts2) """, // """ -@nullableInts='[10,999]' (Size = 4000) +@nullableInts1='10' +@nullableInts2='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].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[Int] NOT IN ( - SELECT [n].[value] - FROM OPENJSON(@nullableInts) WITH ([value] int '$') AS [n] -) +WHERE [p].[Int] NOT IN (@nullableInts1, @nullableInts2) """); } @@ -615,25 +598,19 @@ public override async Task Parameter_collection_of_nullable_ints_Contains_nullab AssertSql( """ -@nullableInts_without_nulls='[999]' (Size = 4000) +@nullableInts2='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].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[NullableInt] IN ( - SELECT [n].[value] - FROM OPENJSON(@nullableInts_without_nulls) AS [n] -) OR [p].[NullableInt] IS NULL +WHERE [p].[NullableInt] IS NULL OR [p].[NullableInt] = @nullableInts2 """, // """ -@nullableInts_without_nulls='[999]' (Size = 4000) +@nullableInts2='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].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[NullableInt] NOT IN ( - SELECT [n].[value] - FROM OPENJSON(@nullableInts_without_nulls) AS [n] -) AND [p].[NullableInt] IS NOT NULL +WHERE [p].[NullableInt] IS NOT NULL AND [p].[NullableInt] <> @nullableInts2 """); } @@ -643,25 +620,21 @@ public override async Task Parameter_collection_of_strings_Contains_string(bool AssertSql( """ -@strings='["10","999"]' (Size = 4000) +@strings1='10' (Size = 4000) +@strings2='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].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[String] IN ( - SELECT [s].[value] - FROM OPENJSON(@strings) WITH ([value] nvarchar(max) '$') AS [s] -) +WHERE [p].[String] IN (@strings1, @strings2) """, // """ -@strings='["10","999"]' (Size = 4000) +@strings1='10' (Size = 4000) +@strings2='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].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[String] NOT IN ( - SELECT [s].[value] - FROM OPENJSON(@strings) WITH ([value] nvarchar(max) '$') AS [s] -) +WHERE [p].[String] NOT IN (@strings1, @strings2) """); } @@ -671,25 +644,21 @@ public override async Task Parameter_collection_of_strings_Contains_nullable_str AssertSql( """ -@strings='["10","999"]' (Size = 4000) +@strings1='10' (Size = 4000) +@strings2='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].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[NullableString] IN ( - SELECT [s].[value] - FROM OPENJSON(@strings) WITH ([value] nvarchar(max) '$') AS [s] -) +WHERE [p].[NullableString] IN (@strings1, @strings2) """, // """ -@strings='["10","999"]' (Size = 4000) +@strings1='10' (Size = 4000) +@strings2='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].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[NullableString] NOT IN ( - SELECT [s].[value] - FROM OPENJSON(@strings) WITH ([value] nvarchar(max) '$') AS [s] -) OR [p].[NullableString] IS NULL +WHERE [p].[NullableString] NOT IN (@strings1, @strings2) OR [p].[NullableString] IS NULL """); } @@ -699,25 +668,19 @@ public override async Task Parameter_collection_of_nullable_strings_Contains_str AssertSql( """ -@strings='["10",null]' (Size = 4000) +@strings1='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].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[String] IN ( - SELECT [s].[value] - FROM OPENJSON(@strings) WITH ([value] nvarchar(max) '$') AS [s] -) +WHERE [p].[String] = @strings1 """, // """ -@strings_without_nulls='["10"]' (Size = 4000) +@strings1='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].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[String] NOT IN ( - SELECT [s].[value] - FROM OPENJSON(@strings_without_nulls) AS [s] -) +WHERE [p].[String] <> @strings1 """); } @@ -727,25 +690,19 @@ public override async Task Parameter_collection_of_nullable_strings_Contains_nul AssertSql( """ -@strings_without_nulls='["999"]' (Size = 4000) +@strings1='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].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[NullableString] IN ( - SELECT [s].[value] - FROM OPENJSON(@strings_without_nulls) AS [s] -) OR [p].[NullableString] IS NULL +WHERE [p].[NullableString] IS NULL OR [p].[NullableString] = @strings1 """, // """ -@strings_without_nulls='["999"]' (Size = 4000) +@strings1='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].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[NullableString] NOT IN ( - SELECT [s].[value] - FROM OPENJSON(@strings_without_nulls) AS [s] -) AND [p].[NullableString] IS NOT NULL +WHERE [p].[NullableString] IS NOT NULL AND [p].[NullableString] <> @strings1 """); } @@ -755,14 +712,12 @@ public override async Task Parameter_collection_of_DateTimes_Contains(bool async AssertSql( """ -@dateTimes='["2020-01-10T12:30:00Z","9999-01-01T00:00:00Z"]' (Size = 4000) +@dateTimes1='2020-01-10T12:30:00.0000000Z' (DbType = DateTime) +@dateTimes2='9999-01-01T00:00:00.0000000Z' (DbType = DateTime) 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[DateTime] IN ( - SELECT [d].[value] - FROM OPENJSON(@dateTimes) WITH ([value] datetime '$') AS [d] -) +WHERE [p].[DateTime] IN (@dateTimes1, @dateTimes2) """); } @@ -772,14 +727,11 @@ public override async Task Parameter_collection_of_bools_Contains(bool async) AssertSql( """ -@bools='[true]' (Size = 4000) +@bools1='True' 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[Bool] IN ( - SELECT [b].[value] - FROM OPENJSON(@bools) WITH ([value] bit '$') AS [b] -) +WHERE [p].[Bool] = @bools1 """); } @@ -789,14 +741,12 @@ public override async Task Parameter_collection_of_enums_Contains(bool async) AssertSql( """ -@enums='[0,3]' (Size = 4000) +@enums1='0' +@enums2='3' 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[Enum] IN ( - SELECT [e].[value] - FROM OPENJSON(@enums) WITH ([value] int '$') AS [e] -) +WHERE [p].[Enum] IN (@enums1, @enums2) """); } @@ -808,10 +758,7 @@ public override async Task Parameter_collection_null_Contains(bool async) """ 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[Int] IN ( - SELECT [i].[value] - FROM OPENJSON(NULL) WITH ([value] int '$') AS [i] -) +WHERE 0 = 1 """); } @@ -857,6 +804,21 @@ SELECT COUNT(*) """); } + public override async Task Parameter_collection_Count_with_huge_number_of_values(bool async) + { + await base.Parameter_collection_Count_with_huge_number_of_values(async); + + Assert.Contains("OPENJSON(@ids) WITH ([value] int '$')", Fixture.TestSqlLoggerFactory.SqlStatements[0], StringComparison.Ordinal); + } + + public override async Task Parameter_collection_of_ints_Contains_int_with_huge_number_of_values(bool async) + { + await base.Parameter_collection_of_ints_Contains_int_with_huge_number_of_values(async); + + Assert.Contains("OPENJSON(@ints) WITH ([value] int '$')", Fixture.TestSqlLoggerFactory.SqlStatements[0], StringComparison.Ordinal); + Assert.Contains("OPENJSON(@ints) WITH ([value] int '$')", Fixture.TestSqlLoggerFactory.SqlStatements[1], StringComparison.Ordinal); + } + public override async Task Column_collection_of_ints_Contains(bool async) { await base.Column_collection_of_ints_Contains(async); @@ -1143,11 +1105,17 @@ public override async Task Parameter_collection_index_Column_equal_Column(bool a AssertSql( """ -@ints='[0,2,3]' (Size = 4000) +@ints1='0' +@ints2='2' +@ints3='3' 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE CAST(JSON_VALUE(@ints, '$[' + CAST([p].[Int] AS nvarchar(max)) + ']') AS int) = [p].[Int] +WHERE ( + SELECT [i].[Value] + FROM (VALUES (1, @ints1), (2, @ints2), (3, @ints3)) AS [i]([_ord], [Value]) + ORDER BY [i].[_ord] + OFFSET [p].[Int] ROWS FETCH NEXT 1 ROWS ONLY) = [p].[Int] """); } @@ -1158,11 +1126,17 @@ public override async Task Parameter_collection_index_Column_equal_constant(bool AssertSql( """ -@ints='[1,2,3]' (Size = 4000) +@ints1='1' +@ints2='2' +@ints3='3' 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE CAST(JSON_VALUE(@ints, '$[' + CAST([p].[Int] AS nvarchar(max)) + ']') AS int) = 1 +WHERE ( + SELECT [i].[Value] + FROM (VALUES (1, @ints1), (2, @ints2), (3, @ints3)) AS [i]([_ord], [Value]) + ORDER BY [i].[_ord] + OFFSET [p].[Int] ROWS FETCH NEXT 1 ROWS ONLY) = 1 """); } @@ -1487,14 +1461,15 @@ public override async Task Column_collection_Join_parameter_collection(bool asyn AssertSql( """ -@ints='[11,111]' (Size = 4000) +@ints1='11' +@ints2='111' 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] WHERE ( SELECT COUNT(*) FROM OPENJSON([p].[Ints]) WITH ([value] int '$') AS [i] - INNER JOIN OPENJSON(@ints) WITH ([value] int '$') AS [i0] ON [i].[value] = [i0].[value]) = 2 + INNER JOIN (VALUES (@ints1), (@ints2)) AS [i0]([Value]) ON [i].[value] = [i0].[Value]) = 2 """); } @@ -1519,7 +1494,8 @@ public override async Task Parameter_collection_Concat_column_collection(bool as AssertSql( """ -@ints='[11,111]' (Size = 4000) +@ints1='11' +@ints2='111' 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] @@ -1527,7 +1503,7 @@ FROM [PrimitiveCollectionsEntity] AS [p] SELECT COUNT(*) FROM ( SELECT 1 AS empty - FROM OPENJSON(@ints) AS [i] + FROM (VALUES (@ints1), (@ints2)) AS [i]([Value]) UNION ALL SELECT 1 AS empty FROM OPENJSON([p].[Ints]) AS [i0] @@ -1541,10 +1517,15 @@ public override async Task Parameter_collection_with_type_inference_for_JsonScal AssertSql( """ -@values='["one","two"]' (Size = 4000) +@values1='one' (Size = 4000) +@values2='two' (Size = 4000) SELECT CASE - WHEN [p].[Id] <> 0 THEN JSON_VALUE(@values, '$[' + CAST([p].[Int] % 2 AS nvarchar(max)) + ']') + WHEN [p].[Id] <> 0 THEN ( + SELECT [v].[Value] + FROM (VALUES (1, @values1), (2, @values2)) AS [v]([_ord], [Value]) + ORDER BY [v].[_ord] + OFFSET [p].[Int] % 2 ROWS FETCH NEXT 1 ROWS ONLY) ELSE N'foo' END FROM [PrimitiveCollectionsEntity] AS [p] @@ -1557,7 +1538,8 @@ public override async Task Column_collection_Union_parameter_collection(bool asy AssertSql( """ -@ints='[11,111]' (Size = 4000) +@ints1='11' +@ints2='111' 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] @@ -1567,8 +1549,8 @@ SELECT COUNT(*) SELECT [i].[value] FROM OPENJSON([p].[Ints]) WITH ([value] int '$') AS [i] UNION - SELECT [i0].[value] - FROM OPENJSON(@ints) WITH ([value] int '$') AS [i0] + SELECT [i0].[Value] AS [value] + FROM (VALUES (@ints1), (@ints2)) AS [i0]([Value]) ) AS [u]) = 2 """); } @@ -1688,22 +1670,23 @@ public override async Task Parameter_collection_in_subquery_Union_column_collect AssertSql( """ -@ints='[10,111]' (Size = 4000) +@ints1='10' +@ints2='111' 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] WHERE ( SELECT COUNT(*) FROM ( - SELECT [i1].[value] + SELECT [i1].[Value] FROM ( - SELECT CAST([i].[value] AS int) AS [value] - FROM OPENJSON(@ints) AS [i] - ORDER BY CAST([i].[key] AS int) + SELECT [i].[Value] + FROM (VALUES (1, @ints1), (2, @ints2)) AS [i]([_ord], [Value]) + ORDER BY [i].[_ord] OFFSET 1 ROWS ) AS [i1] UNION - SELECT [i0].[value] + SELECT [i0].[value] AS [Value] FROM OPENJSON([p].[Ints]) WITH ([value] int '$') AS [i0] ) AS [u]) = 3 """); @@ -1715,17 +1698,17 @@ public override async Task Parameter_collection_in_subquery_Union_column_collect AssertSql( """ -@Skip='[111]' (Size = 4000) +@Skip1='111' 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] WHERE ( SELECT COUNT(*) FROM ( - SELECT [s].[value] - FROM OPENJSON(@Skip) WITH ([value] int '$') AS [s] + SELECT [s].[Value] + FROM (VALUES (@Skip1)) AS [s]([Value]) UNION - SELECT [i].[value] + SELECT [i].[value] AS [Value] FROM OPENJSON([p].[Ints]) WITH ([value] int '$') AS [i] ) AS [u]) = 3 """); @@ -1737,17 +1720,17 @@ public override async Task Parameter_collection_in_subquery_Union_column_collect AssertSql( """ -@Skip='[111]' (Size = 4000) +@Skip1='111' 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] WHERE ( SELECT COUNT(*) FROM ( - SELECT [s].[value] - FROM OPENJSON(@Skip) WITH ([value] int '$') AS [s] + SELECT [s].[Value] + FROM (VALUES (@Skip1)) AS [s]([Value]) UNION - SELECT [i2].[value] + SELECT [i2].[value] AS [Value] FROM ( SELECT TOP(20) [i1].[value] FROM ( @@ -1779,19 +1762,20 @@ public override async Task Parameter_collection_in_subquery_Count_as_compiled_qu // TODO: the subquery projection contains extra columns which we should remove AssertSql( """ -@ints='[10,111]' (Size = 4000) +@ints1='10' +@ints2='111' SELECT COUNT(*) FROM [PrimitiveCollectionsEntity] AS [p] WHERE ( SELECT COUNT(*) FROM ( - SELECT CAST([i].[value] AS int) AS [value0] - FROM OPENJSON(@ints) AS [i] - ORDER BY CAST([i].[key] AS int) + SELECT [i].[Value] AS [Value0] + FROM (VALUES (1, @ints1), (2, @ints2)) AS [i]([_ord], [Value]) + ORDER BY [i].[_ord] OFFSET 1 ROWS ) AS [i0] - WHERE [i0].[value0] > [p].[Id]) = 1 + WHERE [i0].[Value0] > [p].[Id]) = 1 """); } @@ -1808,7 +1792,8 @@ public override async Task Column_collection_in_subquery_Union_parameter_collect AssertSql( """ -@ints='[10,111]' (Size = 4000) +@ints1='10' +@ints2='111' 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] @@ -1823,8 +1808,8 @@ ORDER BY CAST([i].[key] AS int) OFFSET 1 ROWS ) AS [i1] UNION - SELECT [i0].[value] - FROM OPENJSON(@ints) WITH ([value] int '$') AS [i0] + SELECT [i0].[Value] AS [value] + FROM (VALUES (@ints1), (@ints2)) AS [i0]([Value]) ) AS [u]) = 3 """); } @@ -2063,24 +2048,21 @@ public override async Task Project_inline_collection_with_Concat(bool async) public override async Task Nested_contains_with_Lists_and_no_inferred_type_mapping(bool async) { await base.Nested_contains_with_Lists_and_no_inferred_type_mapping(async); - AssertSql( """ -@ints='[1,2,3]' (Size = 4000) -@strings='["one","two","three"]' (Size = 4000) +@ints1='1' +@ints2='2' +@ints3='3' +@strings1='one' (Size = 4000) +@strings2='two' (Size = 4000) +@strings3='three' (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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] WHERE CASE - WHEN [p].[Int] IN ( - SELECT [i].[value] - FROM OPENJSON(@ints) WITH ([value] int '$') AS [i] - ) THEN N'one' + WHEN [p].[Int] IN (@ints1, @ints2, @ints3) THEN N'one' ELSE N'two' -END IN ( - SELECT [s].[value] - FROM OPENJSON(@strings) WITH ([value] nvarchar(max) '$') AS [s] -) +END IN (@strings1, @strings2, @strings3) """); } @@ -2090,21 +2072,19 @@ public override async Task Nested_contains_with_arrays_and_no_inferred_type_mapp AssertSql( """ -@ints='[1,2,3]' (Size = 4000) -@strings='["one","two","three"]' (Size = 4000) +@ints1='1' +@ints2='2' +@ints3='3' +@strings1='one' (Size = 4000) +@strings2='two' (Size = 4000) +@strings3='three' (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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] WHERE CASE - WHEN [p].[Int] IN ( - SELECT [i].[value] - FROM OPENJSON(@ints) WITH ([value] int '$') AS [i] - ) THEN N'one' + WHEN [p].[Int] IN (@ints1, @ints2, @ints3) THEN N'one' ELSE N'two' -END IN ( - SELECT [s].[value] - FROM OPENJSON(@strings) WITH ([value] nvarchar(max) '$') AS [s] -) +END IN (@strings1, @strings2, @strings3) """); } @@ -2114,25 +2094,21 @@ public override async Task Parameter_collection_of_structs_Contains_struct(bool AssertSql( """ -@values='[22,33]' (Size = 4000) +@values1='22' +@values2='33' 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[WrappedId] IN ( - SELECT [v].[value] - FROM OPENJSON(@values) WITH ([value] int '$') AS [v] -) +WHERE [p].[WrappedId] IN (@values1, @values2) """, // """ -@values='[11,44]' (Size = 4000) +@values1='11' +@values2='44' 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[WrappedId] NOT IN ( - SELECT [v].[value] - FROM OPENJSON(@values) WITH ([value] int '$') AS [v] -) +WHERE [p].[WrappedId] NOT IN (@values1, @values2) """); } @@ -2142,25 +2118,21 @@ public override async Task Parameter_collection_of_structs_Contains_nullable_str AssertSql( """ -@values='[22,33]' (Size = 4000) +@values1='22' +@values2='33' 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[NullableWrappedId] IN ( - SELECT [v].[value] - FROM OPENJSON(@values) WITH ([value] int '$') AS [v] -) +WHERE [p].[NullableWrappedId] IN (@values1, @values2) """, // """ -@values='[11,44]' (Size = 4000) +@values1='11' +@values2='44' 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[NullableWrappedId] NOT IN ( - SELECT [v].[value] - FROM OPENJSON(@values) WITH ([value] int '$') AS [v] -) OR [p].[NullableWrappedId] IS NULL +WHERE [p].[NullableWrappedId] NOT IN (@values1, @values2) OR [p].[NullableWrappedId] IS NULL """); } @@ -2170,25 +2142,21 @@ public override async Task Parameter_collection_of_structs_Contains_nullable_str AssertSql( """ -@values='[22,33]' (Size = 4000) +@values1='22' +@values2='33' 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[NullableWrappedIdWithNullableComparer] IN ( - SELECT [v].[value] - FROM OPENJSON(@values) WITH ([value] int '$') AS [v] -) +WHERE [p].[NullableWrappedIdWithNullableComparer] IN (@values1, @values2) """, // """ -@values='[11,44]' (Size = 4000) +@values1='11' +@values2='44' 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[NullableWrappedId] NOT IN ( - SELECT [v].[value] - FROM OPENJSON(@values) WITH ([value] int '$') AS [v] -) OR [p].[NullableWrappedId] IS NULL +WHERE [p].[NullableWrappedId] NOT IN (@values1, @values2) OR [p].[NullableWrappedId] IS NULL """); } @@ -2198,25 +2166,20 @@ public override async Task Parameter_collection_of_nullable_structs_Contains_str AssertSql( """ -@values='[null,22]' (Size = 4000) +@values2='22' 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[WrappedId] IN ( - SELECT [v].[value] - FROM OPENJSON(@values) WITH ([value] int '$') AS [v] -) +WHERE [p].[WrappedId] = @values2 """, // """ -@values='[11,44]' (Size = 4000) +@values1='11' +@values2='44' 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[WrappedId] NOT IN ( - SELECT [v].[value] - FROM OPENJSON(@values) WITH ([value] int '$') AS [v] -) +WHERE [p].[WrappedId] NOT IN (@values1, @values2) """); } @@ -2226,25 +2189,20 @@ public override async Task Parameter_collection_of_nullable_structs_Contains_nul AssertSql( """ -@values_without_nulls='[22]' (Size = 4000) +@values2='22' 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[NullableWrappedId] IN ( - SELECT [v].[value] - FROM OPENJSON(@values_without_nulls) AS [v] -) OR [p].[NullableWrappedId] IS NULL +WHERE [p].[NullableWrappedId] IS NULL OR [p].[NullableWrappedId] = @values2 """, // """ -@values='[11,44]' (Size = 4000) +@values1='11' +@values2='44' 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[NullableWrappedId] NOT IN ( - SELECT [v].[value] - FROM OPENJSON(@values) WITH ([value] int '$') AS [v] -) OR [p].[NullableWrappedId] IS NULL +WHERE [p].[NullableWrappedId] NOT IN (@values1, @values2) OR [p].[NullableWrappedId] IS NULL """); } @@ -2254,25 +2212,20 @@ public override async Task Parameter_collection_of_nullable_structs_Contains_nul AssertSql( """ -@values_without_nulls='[22]' (Size = 4000) +@values2='22' 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[NullableWrappedIdWithNullableComparer] IN ( - SELECT [v].[value] - FROM OPENJSON(@values_without_nulls) AS [v] -) OR [p].[NullableWrappedIdWithNullableComparer] IS NULL +WHERE [p].[NullableWrappedIdWithNullableComparer] IS NULL OR [p].[NullableWrappedIdWithNullableComparer] = @values2 """, // """ -@values='[11,44]' (Size = 4000) +@values1='11' +@values2='44' 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[NullableWrappedIdWithNullableComparer] NOT IN ( - SELECT [v].[value] - FROM OPENJSON(@values) WITH ([value] int '$') AS [v] -) OR [p].[NullableWrappedIdWithNullableComparer] IS NULL +WHERE [p].[NullableWrappedIdWithNullableComparer] NOT IN (@values1, @values2) OR [p].[NullableWrappedIdWithNullableComparer] IS NULL """); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQuerySqlServerJsonTypeTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQuerySqlServerJsonTypeTest.cs index 591b1e1d270..338bad1c1ff 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQuerySqlServerJsonTypeTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQuerySqlServerJsonTypeTest.cs @@ -9,6 +9,8 @@ namespace Microsoft.EntityFrameworkCore.Query; public class PrimitiveCollectionsQuerySqlServerJsonTypeTest : PrimitiveCollectionsQueryRelationalTestBase< PrimitiveCollectionsQuerySqlServerJsonTypeTest.PrimitiveCollectionsQuerySqlServerFixture> { + public override int? NumberOfValuesForHugeParameterCollectionTests { get; } = 5000; + public PrimitiveCollectionsQuerySqlServerJsonTypeTest( PrimitiveCollectionsQuerySqlServerFixture fixture, ITestOutputHelper testOutputHelper) @@ -95,6 +97,21 @@ SELECT COUNT(*) """); } + public override async Task Parameter_collection_Count_with_huge_number_of_values(bool async) + { + await base.Parameter_collection_Count_with_huge_number_of_values(async); + + Assert.Contains("OPENJSON(@ids) WITH ([value] int '$')", Fixture.TestSqlLoggerFactory.SqlStatements[0], StringComparison.Ordinal); + } + + public override async Task Parameter_collection_of_ints_Contains_int_with_huge_number_of_values(bool async) + { + await base.Parameter_collection_of_ints_Contains_int_with_huge_number_of_values(async); + + Assert.Contains("OPENJSON(@ints) WITH ([value] int '$')", Fixture.TestSqlLoggerFactory.SqlStatements[0], StringComparison.Ordinal); + Assert.Contains("OPENJSON(@ints) WITH ([value] int '$')", Fixture.TestSqlLoggerFactory.SqlStatements[1], StringComparison.Ordinal); + } + public override async Task Inline_collection_of_ints_Contains(bool async) { await base.Inline_collection_of_ints_Contains(async); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQuerySqlServerTest.cs index fe39b9fb6ca..92829061f09 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQuerySqlServerTest.cs @@ -6,6 +6,8 @@ namespace Microsoft.EntityFrameworkCore.Query; public class PrimitiveCollectionsQuerySqlServerTest : PrimitiveCollectionsQueryRelationalTestBase< PrimitiveCollectionsQuerySqlServerTest.PrimitiveCollectionsQuerySqlServerFixture> { + public override int? NumberOfValuesForHugeParameterCollectionTests { get; } = 5000; + public PrimitiveCollectionsQuerySqlServerTest(PrimitiveCollectionsQuerySqlServerFixture fixture, ITestOutputHelper testOutputHelper) : base(fixture) { @@ -481,14 +483,15 @@ public override async Task Parameter_collection_Count(bool async) AssertSql( """ -@ids='[2,999]' (Size = 4000) +@ids1='2' +@ids2='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].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] WHERE ( SELECT COUNT(*) - FROM OPENJSON(@ids) WITH ([value] int '$') AS [i] - WHERE [i].[value] > [p].[Id]) = 1 + FROM (VALUES (@ids1), (@ids2)) AS [i]([Value]) + WHERE [i].[Value] > [p].[Id]) = 1 """); } @@ -498,25 +501,21 @@ public override async Task Parameter_collection_of_ints_Contains_int(bool async) AssertSql( """ -@ints='[10,999]' (Size = 4000) +@ints1='10' +@ints2='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].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[Int] IN ( - SELECT [i].[value] - FROM OPENJSON(@ints) WITH ([value] int '$') AS [i] -) +WHERE [p].[Int] IN (@ints1, @ints2) """, // """ -@ints='[10,999]' (Size = 4000) +@ints1='10' +@ints2='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].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[Int] NOT IN ( - SELECT [i].[value] - FROM OPENJSON(@ints) WITH ([value] int '$') AS [i] -) +WHERE [p].[Int] NOT IN (@ints1, @ints2) """); } @@ -526,25 +525,21 @@ public override async Task Parameter_collection_HashSet_of_ints_Contains_int(boo AssertSql( """ -@ints='[10,999]' (Size = 4000) +@ints1='10' +@ints2='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].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[Int] IN ( - SELECT [i].[value] - FROM OPENJSON(@ints) WITH ([value] int '$') AS [i] -) +WHERE [p].[Int] IN (@ints1, @ints2) """, // """ -@ints='[10,999]' (Size = 4000) +@ints1='10' +@ints2='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].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[Int] NOT IN ( - SELECT [i].[value] - FROM OPENJSON(@ints) WITH ([value] int '$') AS [i] -) +WHERE [p].[Int] NOT IN (@ints1, @ints2) """); } @@ -554,25 +549,21 @@ public override async Task Parameter_collection_ImmutableArray_of_ints_Contains_ AssertSql( """ -@ints='[10,999]' (Nullable = false) (Size = 4000) +@ints1='10' +@ints2='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].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[Int] IN ( - SELECT [i].[value] - FROM OPENJSON(@ints) WITH ([value] int '$') AS [i] -) +WHERE [p].[Int] IN (@ints1, @ints2) """, // """ -@ints='[10,999]' (Nullable = false) (Size = 4000) +@ints1='10' +@ints2='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].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[Int] NOT IN ( - SELECT [i].[value] - FROM OPENJSON(@ints) WITH ([value] int '$') AS [i] -) +WHERE [p].[Int] NOT IN (@ints1, @ints2) """); } @@ -582,25 +573,21 @@ public override async Task Parameter_collection_of_ints_Contains_nullable_int(bo AssertSql( """ -@ints='[10,999]' (Size = 4000) +@ints1='10' +@ints2='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].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[NullableInt] IN ( - SELECT [i].[value] - FROM OPENJSON(@ints) WITH ([value] int '$') AS [i] -) +WHERE [p].[NullableInt] IN (@ints1, @ints2) """, // """ -@ints='[10,999]' (Size = 4000) +@ints1='10' +@ints2='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].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[NullableInt] NOT IN ( - SELECT [i].[value] - FROM OPENJSON(@ints) WITH ([value] int '$') AS [i] -) OR [p].[NullableInt] IS NULL +WHERE [p].[NullableInt] NOT IN (@ints1, @ints2) OR [p].[NullableInt] IS NULL """); } @@ -610,25 +597,21 @@ public override async Task Parameter_collection_of_nullable_ints_Contains_int(bo AssertSql( """ -@nullableInts='[10,999]' (Size = 4000) +@nullableInts1='10' +@nullableInts2='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].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[Int] IN ( - SELECT [n].[value] - FROM OPENJSON(@nullableInts) WITH ([value] int '$') AS [n] -) +WHERE [p].[Int] IN (@nullableInts1, @nullableInts2) """, // """ -@nullableInts='[10,999]' (Size = 4000) +@nullableInts1='10' +@nullableInts2='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].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[Int] NOT IN ( - SELECT [n].[value] - FROM OPENJSON(@nullableInts) WITH ([value] int '$') AS [n] -) +WHERE [p].[Int] NOT IN (@nullableInts1, @nullableInts2) """); } @@ -638,25 +621,19 @@ public override async Task Parameter_collection_of_nullable_ints_Contains_nullab AssertSql( """ -@nullableInts_without_nulls='[999]' (Size = 4000) +@nullableInts2='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].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[NullableInt] IN ( - SELECT [n].[value] - FROM OPENJSON(@nullableInts_without_nulls) AS [n] -) OR [p].[NullableInt] IS NULL +WHERE [p].[NullableInt] IS NULL OR [p].[NullableInt] = @nullableInts2 """, // """ -@nullableInts_without_nulls='[999]' (Size = 4000) +@nullableInts2='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].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[NullableInt] NOT IN ( - SELECT [n].[value] - FROM OPENJSON(@nullableInts_without_nulls) AS [n] -) AND [p].[NullableInt] IS NOT NULL +WHERE [p].[NullableInt] IS NOT NULL AND [p].[NullableInt] <> @nullableInts2 """); } @@ -666,25 +643,21 @@ public override async Task Parameter_collection_of_strings_Contains_string(bool AssertSql( """ -@strings='["10","999"]' (Size = 4000) +@strings1='10' (Size = 4000) +@strings2='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].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[String] IN ( - SELECT [s].[value] - FROM OPENJSON(@strings) WITH ([value] nvarchar(max) '$') AS [s] -) +WHERE [p].[String] IN (@strings1, @strings2) """, // """ -@strings='["10","999"]' (Size = 4000) +@strings1='10' (Size = 4000) +@strings2='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].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[String] NOT IN ( - SELECT [s].[value] - FROM OPENJSON(@strings) WITH ([value] nvarchar(max) '$') AS [s] -) +WHERE [p].[String] NOT IN (@strings1, @strings2) """); } @@ -694,25 +667,21 @@ public override async Task Parameter_collection_of_strings_Contains_nullable_str AssertSql( """ -@strings='["10","999"]' (Size = 4000) +@strings1='10' (Size = 4000) +@strings2='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].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[NullableString] IN ( - SELECT [s].[value] - FROM OPENJSON(@strings) WITH ([value] nvarchar(max) '$') AS [s] -) +WHERE [p].[NullableString] IN (@strings1, @strings2) """, // """ -@strings='["10","999"]' (Size = 4000) +@strings1='10' (Size = 4000) +@strings2='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].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[NullableString] NOT IN ( - SELECT [s].[value] - FROM OPENJSON(@strings) WITH ([value] nvarchar(max) '$') AS [s] -) OR [p].[NullableString] IS NULL +WHERE [p].[NullableString] NOT IN (@strings1, @strings2) OR [p].[NullableString] IS NULL """); } @@ -722,25 +691,19 @@ public override async Task Parameter_collection_of_nullable_strings_Contains_str AssertSql( """ -@strings='["10",null]' (Size = 4000) +@strings1='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].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[String] IN ( - SELECT [s].[value] - FROM OPENJSON(@strings) WITH ([value] nvarchar(max) '$') AS [s] -) +WHERE [p].[String] = @strings1 """, // """ -@strings_without_nulls='["10"]' (Size = 4000) +@strings1='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].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[String] NOT IN ( - SELECT [s].[value] - FROM OPENJSON(@strings_without_nulls) AS [s] -) +WHERE [p].[String] <> @strings1 """); } @@ -750,25 +713,19 @@ public override async Task Parameter_collection_of_nullable_strings_Contains_nul AssertSql( """ -@strings_without_nulls='["999"]' (Size = 4000) +@strings1='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].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[NullableString] IN ( - SELECT [s].[value] - FROM OPENJSON(@strings_without_nulls) AS [s] -) OR [p].[NullableString] IS NULL +WHERE [p].[NullableString] IS NULL OR [p].[NullableString] = @strings1 """, // """ -@strings_without_nulls='["999"]' (Size = 4000) +@strings1='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].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[NullableString] NOT IN ( - SELECT [s].[value] - FROM OPENJSON(@strings_without_nulls) AS [s] -) AND [p].[NullableString] IS NOT NULL +WHERE [p].[NullableString] IS NOT NULL AND [p].[NullableString] <> @strings1 """); } @@ -778,14 +735,12 @@ public override async Task Parameter_collection_of_DateTimes_Contains(bool async AssertSql( """ -@dateTimes='["2020-01-10T12:30:00Z","9999-01-01T00:00:00Z"]' (Size = 4000) +@dateTimes1='2020-01-10T12:30:00.0000000Z' (DbType = DateTime) +@dateTimes2='9999-01-01T00:00:00.0000000Z' (DbType = DateTime) 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[DateTime] IN ( - SELECT [d].[value] - FROM OPENJSON(@dateTimes) WITH ([value] datetime '$') AS [d] -) +WHERE [p].[DateTime] IN (@dateTimes1, @dateTimes2) """); } @@ -795,14 +750,11 @@ public override async Task Parameter_collection_of_bools_Contains(bool async) AssertSql( """ -@bools='[true]' (Size = 4000) +@bools1='True' 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[Bool] IN ( - SELECT [b].[value] - FROM OPENJSON(@bools) WITH ([value] bit '$') AS [b] -) +WHERE [p].[Bool] = @bools1 """); } @@ -812,14 +764,12 @@ public override async Task Parameter_collection_of_enums_Contains(bool async) AssertSql( """ -@enums='[0,3]' (Size = 4000) +@enums1='0' +@enums2='3' 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[Enum] IN ( - SELECT [e].[value] - FROM OPENJSON(@enums) WITH ([value] int '$') AS [e] -) +WHERE [p].[Enum] IN (@enums1, @enums2) """); } @@ -831,10 +781,7 @@ public override async Task Parameter_collection_null_Contains(bool async) """ 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[Int] IN ( - SELECT [i].[value] - FROM OPENJSON(NULL) WITH ([value] int '$') AS [i] -) +WHERE 0 = 1 """); } @@ -880,6 +827,21 @@ SELECT COUNT(*) """); } + public override async Task Parameter_collection_Count_with_huge_number_of_values(bool async) + { + await base.Parameter_collection_Count_with_huge_number_of_values(async); + + Assert.Contains("OPENJSON(@ids) WITH ([value] int '$')", Fixture.TestSqlLoggerFactory.SqlStatements[0], StringComparison.Ordinal); + } + + public override async Task Parameter_collection_of_ints_Contains_int_with_huge_number_of_values(bool async) + { + await base.Parameter_collection_of_ints_Contains_int_with_huge_number_of_values(async); + + Assert.Contains("OPENJSON(@ints) WITH ([value] int '$')", Fixture.TestSqlLoggerFactory.SqlStatements[0], StringComparison.Ordinal); + Assert.Contains("OPENJSON(@ints) WITH ([value] int '$')", Fixture.TestSqlLoggerFactory.SqlStatements[1], StringComparison.Ordinal); + } + public override async Task Column_collection_of_ints_Contains(bool async) { await base.Column_collection_of_ints_Contains(async); @@ -1166,11 +1128,17 @@ public override async Task Parameter_collection_index_Column_equal_Column(bool a AssertSql( """ -@ints='[0,2,3]' (Size = 4000) +@ints1='0' +@ints2='2' +@ints3='3' 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE CAST(JSON_VALUE(@ints, '$[' + CAST([p].[Int] AS nvarchar(max)) + ']') AS int) = [p].[Int] +WHERE ( + SELECT [i].[Value] + FROM (VALUES (1, @ints1), (2, @ints2), (3, @ints3)) AS [i]([_ord], [Value]) + ORDER BY [i].[_ord] + OFFSET [p].[Int] ROWS FETCH NEXT 1 ROWS ONLY) = [p].[Int] """); } @@ -1181,11 +1149,17 @@ public override async Task Parameter_collection_index_Column_equal_constant(bool AssertSql( """ -@ints='[1,2,3]' (Size = 4000) +@ints1='1' +@ints2='2' +@ints3='3' 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE CAST(JSON_VALUE(@ints, '$[' + CAST([p].[Int] AS nvarchar(max)) + ']') AS int) = 1 +WHERE ( + SELECT [i].[Value] + FROM (VALUES (1, @ints1), (2, @ints2), (3, @ints3)) AS [i]([_ord], [Value]) + ORDER BY [i].[_ord] + OFFSET [p].[Int] ROWS FETCH NEXT 1 ROWS ONLY) = 1 """); } @@ -1510,14 +1484,15 @@ public override async Task Column_collection_Join_parameter_collection(bool asyn AssertSql( """ -@ints='[11,111]' (Size = 4000) +@ints1='11' +@ints2='111' 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] WHERE ( SELECT COUNT(*) FROM OPENJSON([p].[Ints]) WITH ([value] int '$') AS [i] - INNER JOIN OPENJSON(@ints) WITH ([value] int '$') AS [i0] ON [i].[value] = [i0].[value]) = 2 + INNER JOIN (VALUES (@ints1), (@ints2)) AS [i0]([Value]) ON [i].[value] = [i0].[Value]) = 2 """); } @@ -1542,7 +1517,8 @@ public override async Task Parameter_collection_Concat_column_collection(bool as AssertSql( """ -@ints='[11,111]' (Size = 4000) +@ints1='11' +@ints2='111' 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] @@ -1550,7 +1526,7 @@ FROM [PrimitiveCollectionsEntity] AS [p] SELECT COUNT(*) FROM ( SELECT 1 AS empty - FROM OPENJSON(@ints) AS [i] + FROM (VALUES (@ints1), (@ints2)) AS [i]([Value]) UNION ALL SELECT 1 AS empty FROM OPENJSON([p].[Ints]) AS [i0] @@ -1565,10 +1541,15 @@ public override async Task Parameter_collection_with_type_inference_for_JsonScal AssertSql( """ -@values='["one","two"]' (Size = 4000) +@values1='one' (Size = 4000) +@values2='two' (Size = 4000) SELECT CASE - WHEN [p].[Id] <> 0 THEN JSON_VALUE(@values, '$[' + CAST([p].[Int] % 2 AS nvarchar(max)) + ']') + WHEN [p].[Id] <> 0 THEN ( + SELECT [v].[Value] + FROM (VALUES (1, @values1), (2, @values2)) AS [v]([_ord], [Value]) + ORDER BY [v].[_ord] + OFFSET [p].[Int] % 2 ROWS FETCH NEXT 1 ROWS ONLY) ELSE N'foo' END FROM [PrimitiveCollectionsEntity] AS [p] @@ -1581,7 +1562,8 @@ public override async Task Column_collection_Union_parameter_collection(bool asy AssertSql( """ -@ints='[11,111]' (Size = 4000) +@ints1='11' +@ints2='111' 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] @@ -1591,8 +1573,8 @@ SELECT COUNT(*) SELECT [i].[value] FROM OPENJSON([p].[Ints]) WITH ([value] int '$') AS [i] UNION - SELECT [i0].[value] - FROM OPENJSON(@ints) WITH ([value] int '$') AS [i0] + SELECT [i0].[Value] AS [value] + FROM (VALUES (@ints1), (@ints2)) AS [i0]([Value]) ) AS [u]) = 2 """); } @@ -1712,22 +1694,23 @@ public override async Task Parameter_collection_in_subquery_Union_column_collect AssertSql( """ -@ints='[10,111]' (Size = 4000) +@ints1='10' +@ints2='111' 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] WHERE ( SELECT COUNT(*) FROM ( - SELECT [i1].[value] + SELECT [i1].[Value] FROM ( - SELECT CAST([i].[value] AS int) AS [value] - FROM OPENJSON(@ints) AS [i] - ORDER BY CAST([i].[key] AS int) + SELECT [i].[Value] + FROM (VALUES (1, @ints1), (2, @ints2)) AS [i]([_ord], [Value]) + ORDER BY [i].[_ord] OFFSET 1 ROWS ) AS [i1] UNION - SELECT [i0].[value] + SELECT [i0].[value] AS [Value] FROM OPENJSON([p].[Ints]) WITH ([value] int '$') AS [i0] ) AS [u]) = 3 """); @@ -1739,17 +1722,17 @@ public override async Task Parameter_collection_in_subquery_Union_column_collect AssertSql( """ -@Skip='[111]' (Size = 4000) +@Skip1='111' 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] WHERE ( SELECT COUNT(*) FROM ( - SELECT [s].[value] - FROM OPENJSON(@Skip) WITH ([value] int '$') AS [s] + SELECT [s].[Value] + FROM (VALUES (@Skip1)) AS [s]([Value]) UNION - SELECT [i].[value] + SELECT [i].[value] AS [Value] FROM OPENJSON([p].[Ints]) WITH ([value] int '$') AS [i] ) AS [u]) = 3 """); @@ -1761,17 +1744,17 @@ public override async Task Parameter_collection_in_subquery_Union_column_collect AssertSql( """ -@Skip='[111]' (Size = 4000) +@Skip1='111' 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] WHERE ( SELECT COUNT(*) FROM ( - SELECT [s].[value] - FROM OPENJSON(@Skip) WITH ([value] int '$') AS [s] + SELECT [s].[Value] + FROM (VALUES (@Skip1)) AS [s]([Value]) UNION - SELECT [i2].[value] + SELECT [i2].[value] AS [Value] FROM ( SELECT TOP(20) [i1].[value] FROM ( @@ -1803,19 +1786,20 @@ public override async Task Parameter_collection_in_subquery_Count_as_compiled_qu // TODO: the subquery projection contains extra columns which we should remove AssertSql( """ -@ints='[10,111]' (Size = 4000) +@ints1='10' +@ints2='111' SELECT COUNT(*) FROM [PrimitiveCollectionsEntity] AS [p] WHERE ( SELECT COUNT(*) FROM ( - SELECT CAST([i].[value] AS int) AS [value0] - FROM OPENJSON(@ints) AS [i] - ORDER BY CAST([i].[key] AS int) + SELECT [i].[Value] AS [Value0] + FROM (VALUES (1, @ints1), (2, @ints2)) AS [i]([_ord], [Value]) + ORDER BY [i].[_ord] OFFSET 1 ROWS ) AS [i0] - WHERE [i0].[value0] > [p].[Id]) = 1 + WHERE [i0].[Value0] > [p].[Id]) = 1 """); } @@ -1832,7 +1816,8 @@ public override async Task Column_collection_in_subquery_Union_parameter_collect AssertSql( """ -@ints='[10,111]' (Size = 4000) +@ints1='10' +@ints2='111' 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] @@ -1847,8 +1832,8 @@ ORDER BY CAST([i].[key] AS int) OFFSET 1 ROWS ) AS [i1] UNION - SELECT [i0].[value] - FROM OPENJSON(@ints) WITH ([value] int '$') AS [i0] + SELECT [i0].[Value] AS [value] + FROM (VALUES (@ints1), (@ints2)) AS [i0]([Value]) ) AS [u]) = 3 """); } @@ -2090,21 +2075,19 @@ public override async Task Nested_contains_with_Lists_and_no_inferred_type_mappi AssertSql( """ -@ints='[1,2,3]' (Size = 4000) -@strings='["one","two","three"]' (Size = 4000) +@ints1='1' +@ints2='2' +@ints3='3' +@strings1='one' (Size = 4000) +@strings2='two' (Size = 4000) +@strings3='three' (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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] WHERE CASE - WHEN [p].[Int] IN ( - SELECT [i].[value] - FROM OPENJSON(@ints) WITH ([value] int '$') AS [i] - ) THEN N'one' + WHEN [p].[Int] IN (@ints1, @ints2, @ints3) THEN N'one' ELSE N'two' -END IN ( - SELECT [s].[value] - FROM OPENJSON(@strings) WITH ([value] nvarchar(max) '$') AS [s] -) +END IN (@strings1, @strings2, @strings3) """); } @@ -2114,21 +2097,19 @@ public override async Task Nested_contains_with_arrays_and_no_inferred_type_mapp AssertSql( """ -@ints='[1,2,3]' (Size = 4000) -@strings='["one","two","three"]' (Size = 4000) +@ints1='1' +@ints2='2' +@ints3='3' +@strings1='one' (Size = 4000) +@strings2='two' (Size = 4000) +@strings3='three' (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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] WHERE CASE - WHEN [p].[Int] IN ( - SELECT [i].[value] - FROM OPENJSON(@ints) WITH ([value] int '$') AS [i] - ) THEN N'one' + WHEN [p].[Int] IN (@ints1, @ints2, @ints3) THEN N'one' ELSE N'two' -END IN ( - SELECT [s].[value] - FROM OPENJSON(@strings) WITH ([value] nvarchar(max) '$') AS [s] -) +END IN (@strings1, @strings2, @strings3) """); } @@ -2138,25 +2119,21 @@ public override async Task Parameter_collection_of_structs_Contains_struct(bool AssertSql( """ -@values='[22,33]' (Size = 4000) +@values1='22' +@values2='33' 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[WrappedId] IN ( - SELECT [v].[value] - FROM OPENJSON(@values) WITH ([value] int '$') AS [v] -) +WHERE [p].[WrappedId] IN (@values1, @values2) """, // """ -@values='[11,44]' (Size = 4000) +@values1='11' +@values2='44' 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[WrappedId] NOT IN ( - SELECT [v].[value] - FROM OPENJSON(@values) WITH ([value] int '$') AS [v] -) +WHERE [p].[WrappedId] NOT IN (@values1, @values2) """); } @@ -2166,25 +2143,21 @@ public override async Task Parameter_collection_of_structs_Contains_nullable_str AssertSql( """ -@values='[22,33]' (Size = 4000) +@values1='22' +@values2='33' 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[NullableWrappedId] IN ( - SELECT [v].[value] - FROM OPENJSON(@values) WITH ([value] int '$') AS [v] -) +WHERE [p].[NullableWrappedId] IN (@values1, @values2) """, // """ -@values='[11,44]' (Size = 4000) +@values1='11' +@values2='44' 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[NullableWrappedId] NOT IN ( - SELECT [v].[value] - FROM OPENJSON(@values) WITH ([value] int '$') AS [v] -) OR [p].[NullableWrappedId] IS NULL +WHERE [p].[NullableWrappedId] NOT IN (@values1, @values2) OR [p].[NullableWrappedId] IS NULL """); } @@ -2194,25 +2167,21 @@ public override async Task Parameter_collection_of_structs_Contains_nullable_str AssertSql( """ -@values='[22,33]' (Size = 4000) +@values1='22' +@values2='33' 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[NullableWrappedIdWithNullableComparer] IN ( - SELECT [v].[value] - FROM OPENJSON(@values) WITH ([value] int '$') AS [v] -) +WHERE [p].[NullableWrappedIdWithNullableComparer] IN (@values1, @values2) """, // """ -@values='[11,44]' (Size = 4000) +@values1='11' +@values2='44' 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[NullableWrappedId] NOT IN ( - SELECT [v].[value] - FROM OPENJSON(@values) WITH ([value] int '$') AS [v] -) OR [p].[NullableWrappedId] IS NULL +WHERE [p].[NullableWrappedId] NOT IN (@values1, @values2) OR [p].[NullableWrappedId] IS NULL """); } @@ -2222,25 +2191,20 @@ public override async Task Parameter_collection_of_nullable_structs_Contains_str AssertSql( """ -@values='[null,22]' (Size = 4000) +@values2='22' 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[WrappedId] IN ( - SELECT [v].[value] - FROM OPENJSON(@values) WITH ([value] int '$') AS [v] -) +WHERE [p].[WrappedId] = @values2 """, // """ -@values='[11,44]' (Size = 4000) +@values1='11' +@values2='44' 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[WrappedId] NOT IN ( - SELECT [v].[value] - FROM OPENJSON(@values) WITH ([value] int '$') AS [v] -) +WHERE [p].[WrappedId] NOT IN (@values1, @values2) """); } @@ -2250,25 +2214,20 @@ public override async Task Parameter_collection_of_nullable_structs_Contains_nul AssertSql( """ -@values_without_nulls='[22]' (Size = 4000) +@values2='22' 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[NullableWrappedId] IN ( - SELECT [v].[value] - FROM OPENJSON(@values_without_nulls) AS [v] -) OR [p].[NullableWrappedId] IS NULL +WHERE [p].[NullableWrappedId] IS NULL OR [p].[NullableWrappedId] = @values2 """, // """ -@values='[11,44]' (Size = 4000) +@values1='11' +@values2='44' 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[NullableWrappedId] NOT IN ( - SELECT [v].[value] - FROM OPENJSON(@values) WITH ([value] int '$') AS [v] -) OR [p].[NullableWrappedId] IS NULL +WHERE [p].[NullableWrappedId] NOT IN (@values1, @values2) OR [p].[NullableWrappedId] IS NULL """); } @@ -2278,25 +2237,20 @@ public override async Task Parameter_collection_of_nullable_structs_Contains_nul AssertSql( """ -@values_without_nulls='[22]' (Size = 4000) +@values2='22' 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[NullableWrappedIdWithNullableComparer] IN ( - SELECT [v].[value] - FROM OPENJSON(@values_without_nulls) AS [v] -) OR [p].[NullableWrappedIdWithNullableComparer] IS NULL +WHERE [p].[NullableWrappedIdWithNullableComparer] IS NULL OR [p].[NullableWrappedIdWithNullableComparer] = @values2 """, // """ -@values='[11,44]' (Size = 4000) +@values1='11' +@values2='44' 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].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] FROM [PrimitiveCollectionsEntity] AS [p] -WHERE [p].[NullableWrappedIdWithNullableComparer] NOT IN ( - SELECT [v].[value] - FROM OPENJSON(@values) WITH ([value] int '$') AS [v] -) OR [p].[NullableWrappedIdWithNullableComparer] IS NULL +WHERE [p].[NullableWrappedIdWithNullableComparer] NOT IN (@values1, @values2) OR [p].[NullableWrappedIdWithNullableComparer] IS NULL """); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/QueryFilterFuncletizationSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/QueryFilterFuncletizationSqlServerTest.cs index 9660cdde8d7..edbf79c7720 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/QueryFilterFuncletizationSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/QueryFilterFuncletizationSqlServerTest.cs @@ -98,43 +98,30 @@ public override void DbContext_list_is_parameterized() """ SELECT [l].[Id], [l].[Tenant] FROM [ListFilter] AS [l] -WHERE [l].[Tenant] IN ( - SELECT [e].[value] - FROM OPENJSON(NULL) WITH ([value] int '$') AS [e] -) +WHERE 0 = 1 """, // """ -@ef_filter__TenantIds='[]' (Size = 4000) - SELECT [l].[Id], [l].[Tenant] FROM [ListFilter] AS [l] -WHERE [l].[Tenant] IN ( - SELECT [e].[value] - FROM OPENJSON(@ef_filter__TenantIds) WITH ([value] int '$') AS [e] -) +WHERE 0 = 1 """, // """ -@ef_filter__TenantIds='[1]' (Size = 4000) +@ef_filter__TenantIds1='1' SELECT [l].[Id], [l].[Tenant] FROM [ListFilter] AS [l] -WHERE [l].[Tenant] IN ( - SELECT [e].[value] - FROM OPENJSON(@ef_filter__TenantIds) WITH ([value] int '$') AS [e] -) +WHERE [l].[Tenant] = @ef_filter__TenantIds1 """, // """ -@ef_filter__TenantIds='[2,3]' (Size = 4000) +@ef_filter__TenantIds1='2' +@ef_filter__TenantIds2='3' SELECT [l].[Id], [l].[Tenant] FROM [ListFilter] AS [l] -WHERE [l].[Tenant] IN ( - SELECT [e].[value] - FROM OPENJSON(@ef_filter__TenantIds) WITH ([value] int '$') AS [e] -) +WHERE [l].[Tenant] IN (@ef_filter__TenantIds1, @ef_filter__TenantIds2) """); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/TPCGearsOfWarQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/TPCGearsOfWarQuerySqlServerTest.cs index b4c87879bfa..746fc520fd6 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/TPCGearsOfWarQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/TPCGearsOfWarQuerySqlServerTest.cs @@ -313,7 +313,12 @@ FROM [Tags] AS [t] """, // """ -@tags='["34c8d86e-a4ac-4be5-827f-584dda348a07","df36f493-463f-4123-83f9-6b135deeb7ba","a8ad98f9-e023-4e2a-9a70-c2728455bd34","70534e05-782c-4052-8720-c2c54481ce5f","a7be028a-0cf2-448f-ab55-ce8bc5d8cf69","b39a6fba-9026-4d69-828e-fd7068673e57"]' (Size = 4000) +@tags1='34c8d86e-a4ac-4be5-827f-584dda348a07' +@tags2='df36f493-463f-4123-83f9-6b135deeb7ba' +@tags3='a8ad98f9-e023-4e2a-9a70-c2728455bd34' +@tags4='70534e05-782c-4052-8720-c2c54481ce5f' +@tags5='a7be028a-0cf2-448f-ab55-ce8bc5d8cf69' +@tags6='b39a6fba-9026-4d69-828e-fd7068673e57' SELECT [u].[Nickname], [u].[SquadId], [u].[AssignedCityName], [u].[CityOfBirthName], [u].[FullName], [u].[HasSoulPatch], [u].[LeaderNickname], [u].[LeaderSquadId], [u].[Rank], [u].[Discriminator], [t].[Id], [t].[GearNickName], [t].[GearSquadId], [t].[IssueDate], [t].[Note] FROM ( @@ -324,10 +329,7 @@ UNION ALL FROM [Officers] AS [o] ) AS [u] LEFT JOIN [Tags] AS [t] ON [u].[Nickname] = [t].[GearNickName] AND [u].[SquadId] = [t].[GearSquadId] -WHERE [t].[Id] IS NOT NULL AND [t].[Id] IN ( - SELECT [t0].[value] - FROM OPENJSON(@tags) WITH ([value] uniqueidentifier '$') AS [t0] -) +WHERE [t].[Id] IS NOT NULL AND [t].[Id] IN (@tags1, @tags2, @tags3, @tags4, @tags5, @tags6) """); } @@ -342,7 +344,12 @@ FROM [Tags] AS [t] """, // """ -@tags='["34c8d86e-a4ac-4be5-827f-584dda348a07","df36f493-463f-4123-83f9-6b135deeb7ba","a8ad98f9-e023-4e2a-9a70-c2728455bd34","70534e05-782c-4052-8720-c2c54481ce5f","a7be028a-0cf2-448f-ab55-ce8bc5d8cf69","b39a6fba-9026-4d69-828e-fd7068673e57"]' (Size = 4000) +@tags1='34c8d86e-a4ac-4be5-827f-584dda348a07' +@tags2='df36f493-463f-4123-83f9-6b135deeb7ba' +@tags3='a8ad98f9-e023-4e2a-9a70-c2728455bd34' +@tags4='70534e05-782c-4052-8720-c2c54481ce5f' +@tags5='a7be028a-0cf2-448f-ab55-ce8bc5d8cf69' +@tags6='b39a6fba-9026-4d69-828e-fd7068673e57' SELECT [u].[Nickname], [u].[SquadId], [u].[AssignedCityName], [u].[CityOfBirthName], [u].[FullName], [u].[HasSoulPatch], [u].[LeaderNickname], [u].[LeaderSquadId], [u].[Rank], [u].[Discriminator], [t].[Id], [t].[GearNickName], [t].[GearSquadId], [t].[IssueDate], [t].[Note] FROM ( @@ -354,10 +361,7 @@ FROM [Officers] AS [o] ) AS [u] INNER JOIN [Cities] AS [c] ON [u].[CityOfBirthName] = [c].[Name] LEFT JOIN [Tags] AS [t] ON [u].[Nickname] = [t].[GearNickName] AND [u].[SquadId] = [t].[GearSquadId] -WHERE [c].[Location] IS NOT NULL AND [t].[Id] IN ( - SELECT [t0].[value] - FROM OPENJSON(@tags) WITH ([value] uniqueidentifier '$') AS [t0] -) +WHERE [c].[Location] IS NOT NULL AND [t].[Id] IN (@tags1, @tags2, @tags3, @tags4, @tags5, @tags6) """); } @@ -372,7 +376,12 @@ FROM [Tags] AS [t] """, // """ -@tags='["34c8d86e-a4ac-4be5-827f-584dda348a07","df36f493-463f-4123-83f9-6b135deeb7ba","a8ad98f9-e023-4e2a-9a70-c2728455bd34","70534e05-782c-4052-8720-c2c54481ce5f","a7be028a-0cf2-448f-ab55-ce8bc5d8cf69","b39a6fba-9026-4d69-828e-fd7068673e57"]' (Size = 4000) +@tags1='34c8d86e-a4ac-4be5-827f-584dda348a07' +@tags2='df36f493-463f-4123-83f9-6b135deeb7ba' +@tags3='a8ad98f9-e023-4e2a-9a70-c2728455bd34' +@tags4='70534e05-782c-4052-8720-c2c54481ce5f' +@tags5='a7be028a-0cf2-448f-ab55-ce8bc5d8cf69' +@tags6='b39a6fba-9026-4d69-828e-fd7068673e57' SELECT [u].[Nickname], [u].[SquadId], [u].[AssignedCityName], [u].[CityOfBirthName], [u].[FullName], [u].[HasSoulPatch], [u].[LeaderNickname], [u].[LeaderSquadId], [u].[Rank], [u].[Discriminator] FROM ( @@ -383,10 +392,7 @@ UNION ALL FROM [Officers] AS [o] ) AS [u] LEFT JOIN [Tags] AS [t] ON [u].[Nickname] = [t].[GearNickName] AND [u].[SquadId] = [t].[GearSquadId] -WHERE [t].[Id] IS NOT NULL AND [t].[Id] IN ( - SELECT [t0].[value] - FROM OPENJSON(@tags) WITH ([value] uniqueidentifier '$') AS [t0] -) +WHERE [t].[Id] IS NOT NULL AND [t].[Id] IN (@tags1, @tags2, @tags3, @tags4, @tags5, @tags6) """); } @@ -2402,14 +2408,12 @@ public override async Task Non_unicode_string_literals_in_contains_is_used_for_n AssertSql( """ -@cities='["Unknown","Jacinto\u0027s location","Ephyra\u0027s location"]' (Size = 4000) +@cities1='Unknown' (Size = 100) (DbType = AnsiString) +@cities2='Jacinto's location' (Size = 100) (DbType = AnsiString), @cities3='Ephyra's location' (Size = 100) (DbType = AnsiString) SELECT [c].[Name], [c].[Location], [c].[Nation] FROM [Cities] AS [c] -WHERE [c].[Location] IN ( - SELECT [c0].[value] - FROM OPENJSON(@cities) WITH ([value] varchar(100) '$') AS [c0] -) +WHERE [c].[Location] IN (@cities1, @cities2, @cities3) """); } @@ -3430,14 +3434,13 @@ public override async Task Contains_with_local_nullable_guid_list_closure(bool a AssertSql( """ -@ids='["df36f493-463f-4123-83f9-6b135deeb7ba","23cbcf9b-ce14-45cf-aafa-2c2667ebfdd3","ab1b82d7-88db-42bd-a132-7eef9aa68af4"]' (Size = 4000) +@ids1='df36f493-463f-4123-83f9-6b135deeb7ba' +@ids2='23cbcf9b-ce14-45cf-aafa-2c2667ebfdd3' +@ids3='ab1b82d7-88db-42bd-a132-7eef9aa68af4' SELECT [t].[Id], [t].[GearNickName], [t].[GearSquadId], [t].[IssueDate], [t].[Note] FROM [Tags] AS [t] -WHERE [t].[Id] IN ( - SELECT [i].[value] - FROM OPENJSON(@ids) WITH ([value] uniqueidentifier '$') AS [i] -) +WHERE [t].[Id] IN (@ids1, @ids2, @ids3) """); } @@ -4083,7 +4086,7 @@ public override async Task Contains_on_nullable_array_produces_correct_sql(bool AssertSql( """ -@cities_without_nulls='["Ephyra"]' (Size = 4000) +@cities1='Ephyra' (Size = 450) SELECT [u].[Nickname], [u].[SquadId], [u].[AssignedCityName], [u].[CityOfBirthName], [u].[FullName], [u].[HasSoulPatch], [u].[LeaderNickname], [u].[LeaderSquadId], [u].[Rank], [u].[Discriminator] FROM ( @@ -4094,10 +4097,7 @@ UNION ALL FROM [Officers] AS [o] ) AS [u] LEFT JOIN [Cities] AS [c] ON [u].[AssignedCityName] = [c].[Name] -WHERE [u].[SquadId] < 2 AND ([c].[Name] IN ( - SELECT [c0].[value] - FROM OPENJSON(@cities_without_nulls) AS [c0] -) OR [c].[Name] IS NULL) +WHERE [u].[SquadId] < 2 AND ([c].[Name] IS NULL OR [c].[Name] = @cities1) """); } @@ -7427,8 +7427,6 @@ public override async Task Correlated_collection_with_complex_order_by_funcletiz AssertSql( """ -@nicknames='[]' (Size = 4000) - SELECT [u].[Nickname], [u].[SquadId], [w].[Name], [w].[Id] FROM ( SELECT [g].[Nickname], [g].[SquadId], [g].[FullName] @@ -7438,13 +7436,7 @@ UNION ALL FROM [Officers] AS [o] ) AS [u] LEFT JOIN [Weapons] AS [w] ON [u].[FullName] = [w].[OwnerFullName] -ORDER BY CASE - WHEN [u].[Nickname] IN ( - SELECT [n].[value] - FROM OPENJSON(@nicknames) WITH ([value] nvarchar(450) '$') AS [n] - ) THEN CAST(1 AS bit) - ELSE CAST(0 AS bit) -END DESC, [u].[Nickname], [u].[SquadId] +ORDER BY [u].[Nickname], [u].[SquadId] """); } @@ -8274,14 +8266,11 @@ public override async Task DateTimeOffset_Contains_Less_than_Greater_than(bool a """ @start='1902-01-01T10:00:00.1234567+01:30' @end='1902-01-03T10:00:00.1234567+01:30' -@dates='["1902-01-02T10:00:00.1234567+01:30"]' (Size = 4000) +@dates1='1902-01-02T10:00:00.1234567+01:30' SELECT [m].[Id], [m].[CodeName], [m].[Date], [m].[Difficulty], [m].[Duration], [m].[Rating], [m].[Time], [m].[Timeline] FROM [Missions] AS [m] -WHERE @start <= CAST(CONVERT(date, [m].[Timeline]) AS datetimeoffset) AND [m].[Timeline] < @end AND [m].[Timeline] IN ( - SELECT [d].[value] - FROM OPENJSON(@dates) WITH ([value] datetimeoffset '$') AS [d] -) +WHERE @start <= CAST(CONVERT(date, [m].[Timeline]) AS datetimeoffset) AND [m].[Timeline] < @end AND [m].[Timeline] = @dates1 """); } @@ -9150,8 +9139,6 @@ public override async Task OrderBy_Contains_empty_list(bool async) AssertSql( """ -@ids='[]' (Size = 4000) - SELECT [u].[Nickname], [u].[SquadId], [u].[AssignedCityName], [u].[CityOfBirthName], [u].[FullName], [u].[HasSoulPatch], [u].[LeaderNickname], [u].[LeaderSquadId], [u].[Rank], [u].[Discriminator] FROM ( SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank], N'Gear' AS [Discriminator] @@ -9160,13 +9147,6 @@ UNION ALL SELECT [o].[Nickname], [o].[SquadId], [o].[AssignedCityName], [o].[CityOfBirthName], [o].[FullName], [o].[HasSoulPatch], [o].[LeaderNickname], [o].[LeaderSquadId], [o].[Rank], N'Officer' AS [Discriminator] FROM [Officers] AS [o] ) AS [u] -ORDER BY CASE - WHEN [u].[SquadId] IN ( - SELECT [i].[value] - FROM OPENJSON(@ids) WITH ([value] int '$') AS [i] - ) THEN CAST(1 AS bit) - ELSE CAST(0 AS bit) -END """); } @@ -9965,15 +9945,12 @@ public override async Task Enum_array_contains(bool async) AssertSql( """ -@types_without_nulls='[1]' (Size = 4000) +@types2='1' SELECT [w].[Id], [w].[AmmunitionType], [w].[IsAutomatic], [w].[Name], [w].[OwnerFullName], [w].[SynergyWithId] FROM [Weapons] AS [w] LEFT JOIN [Weapons] AS [w0] ON [w].[SynergyWithId] = [w0].[Id] -WHERE [w0].[Id] IS NOT NULL AND ([w0].[AmmunitionType] IN ( - SELECT [t].[value] - FROM OPENJSON(@types_without_nulls) AS [t] -) OR [w0].[AmmunitionType] IS NULL) +WHERE [w0].[Id] IS NOT NULL AND ([w0].[AmmunitionType] IS NULL OR [w0].[AmmunitionType] = @types2) """); } @@ -10853,7 +10830,8 @@ public override async Task Where_bool_column_and_Contains(bool async) AssertSql( """ -@values='[false,true]' (Size = 4000) +@values1='False' +@values2='True' SELECT [u].[Nickname], [u].[SquadId], [u].[AssignedCityName], [u].[CityOfBirthName], [u].[FullName], [u].[HasSoulPatch], [u].[LeaderNickname], [u].[LeaderSquadId], [u].[Rank], [u].[Discriminator] FROM ( @@ -10863,10 +10841,7 @@ UNION ALL SELECT [o].[Nickname], [o].[SquadId], [o].[AssignedCityName], [o].[CityOfBirthName], [o].[FullName], [o].[HasSoulPatch], [o].[LeaderNickname], [o].[LeaderSquadId], [o].[Rank], N'Officer' AS [Discriminator] FROM [Officers] AS [o] ) AS [u] -WHERE [u].[HasSoulPatch] = CAST(1 AS bit) AND [u].[HasSoulPatch] IN ( - SELECT [v].[value] - FROM OPENJSON(@values) WITH ([value] bit '$') AS [v] -) +WHERE [u].[HasSoulPatch] = CAST(1 AS bit) AND [u].[HasSoulPatch] IN (@values1, @values2) """); } @@ -10876,7 +10851,8 @@ public override async Task Where_bool_column_or_Contains(bool async) AssertSql( """ -@values='[false,true]' (Size = 4000) +@values1='False' +@values2='True' SELECT [u].[Nickname], [u].[SquadId], [u].[AssignedCityName], [u].[CityOfBirthName], [u].[FullName], [u].[HasSoulPatch], [u].[LeaderNickname], [u].[LeaderSquadId], [u].[Rank], [u].[Discriminator] FROM ( @@ -10886,10 +10862,7 @@ UNION ALL SELECT [o].[Nickname], [o].[SquadId], [o].[AssignedCityName], [o].[CityOfBirthName], [o].[FullName], [o].[HasSoulPatch], [o].[LeaderNickname], [o].[LeaderSquadId], [o].[Rank], N'Officer' AS [Discriminator] FROM [Officers] AS [o] ) AS [u] -WHERE [u].[HasSoulPatch] = CAST(1 AS bit) AND [u].[HasSoulPatch] IN ( - SELECT [v].[value] - FROM OPENJSON(@values) WITH ([value] bit '$') AS [v] -) +WHERE [u].[HasSoulPatch] = CAST(1 AS bit) AND [u].[HasSoulPatch] IN (@values1, @values2) """); } @@ -12215,7 +12188,8 @@ public override async Task Nav_expansion_inside_Contains_argument(bool async) AssertSql( """ -@numbers='[1,-1]' (Size = 4000) +@numbers1='1' +@numbers2='-1' SELECT [u].[Nickname], [u].[SquadId], [u].[AssignedCityName], [u].[CityOfBirthName], [u].[FullName], [u].[HasSoulPatch], [u].[LeaderNickname], [u].[LeaderSquadId], [u].[Rank], [u].[Discriminator] FROM ( @@ -12231,10 +12205,7 @@ SELECT 1 FROM [Weapons] AS [w] WHERE [u].[FullName] = [w].[OwnerFullName]) THEN 1 ELSE 0 -END IN ( - SELECT [n].[value] - FROM OPENJSON(@numbers) WITH ([value] int '$') AS [n] -) +END IN (@numbers1, @numbers2) """); } @@ -12244,7 +12215,7 @@ public override async Task Nav_expansion_with_member_pushdown_inside_Contains_ar AssertSql( """ -@weapons='["Marcus\u0027 Lancer","Dom\u0027s Gnasher"]' (Size = 4000) +@weapons1='Marcus' Lancer' (Size = 4000), @weapons2='Dom's Gnasher' (Size = 4000) SELECT [u].[Nickname], [u].[SquadId], [u].[AssignedCityName], [u].[CityOfBirthName], [u].[FullName], [u].[HasSoulPatch], [u].[LeaderNickname], [u].[LeaderSquadId], [u].[Rank], [u].[Discriminator] FROM ( @@ -12258,10 +12229,7 @@ FROM [Officers] AS [o] SELECT TOP(1) [w0].[Name] FROM [Weapons] AS [w0] WHERE [u].[FullName] = [w0].[OwnerFullName] - ORDER BY [w0].[Id]) IN ( - SELECT [w].[value] - FROM OPENJSON(@weapons) WITH ([value] nvarchar(max) '$') AS [w] -) + ORDER BY [w0].[Id]) IN (@weapons1, @weapons2) """); } @@ -12271,7 +12239,9 @@ public override async Task Subquery_inside_Take_argument(bool async) AssertSql( """ -@numbers='[0,1,2]' (Size = 4000) +@numbers1='0' +@numbers2='1' +@numbers3='2' SELECT [u].[Nickname], [u].[SquadId], [w1].[Id], [w1].[AmmunitionType], [w1].[IsAutomatic], [w1].[Name], [w1].[OwnerFullName], [w1].[SynergyWithId] FROM ( @@ -12288,9 +12258,9 @@ LEFT JOIN ( FROM [Weapons] AS [w] ) AS [w0] WHERE [w0].[row] <= ISNULL(( - SELECT [n].[value] - FROM OPENJSON(@numbers) WITH ([value] int '$') AS [n] - ORDER BY [n].[value] + SELECT [n].[Value] + FROM (VALUES (1, @numbers1), (2, @numbers2), (3, @numbers3)) AS [n]([_ord], [Value]) + ORDER BY [n].[Value] OFFSET 1 ROWS FETCH NEXT 1 ROWS ONLY), 0) ) AS [w1] ON [u].[FullName] = [w1].[OwnerFullName] ORDER BY [u].[Nickname], [u].[SquadId], [w1].[OwnerFullName], [w1].[Id] @@ -12542,9 +12512,10 @@ public override async Task Nested_contains_with_enum(bool async) AssertSql( """ -@ranks='[1]' (Size = 4000) +@ranks1='1' @key='5f221fb9-66f4-442a-92c9-d97ed5989cc7' -@keys='["0a47bcb7-a1cb-4345-8944-c58f82d6aac7","5f221fb9-66f4-442a-92c9-d97ed5989cc7"]' (Size = 4000) +@keys1='0a47bcb7-a1cb-4345-8944-c58f82d6aac7' +@keys2='5f221fb9-66f4-442a-92c9-d97ed5989cc7' SELECT [u].[Nickname], [u].[SquadId], [u].[AssignedCityName], [u].[CityOfBirthName], [u].[FullName], [u].[HasSoulPatch], [u].[LeaderNickname], [u].[LeaderSquadId], [u].[Rank], [u].[Discriminator] FROM ( @@ -12555,34 +12526,23 @@ UNION ALL FROM [Officers] AS [o] ) AS [u] WHERE CASE - WHEN [u].[Rank] IN ( - SELECT [r].[value] - FROM OPENJSON(@ranks) WITH ([value] int '$') AS [r] - ) THEN @key + WHEN [u].[Rank] = @ranks1 THEN @key ELSE @key -END IN ( - SELECT [k].[value] - FROM OPENJSON(@keys) WITH ([value] uniqueidentifier '$') AS [k] -) +END IN (@keys1, @keys2) """, // """ -@ammoTypes='[1]' (Size = 4000) +@ammoTypes1='1' @key='5f221fb9-66f4-442a-92c9-d97ed5989cc7' -@keys='["0a47bcb7-a1cb-4345-8944-c58f82d6aac7","5f221fb9-66f4-442a-92c9-d97ed5989cc7"]' (Size = 4000) +@keys1='0a47bcb7-a1cb-4345-8944-c58f82d6aac7' +@keys2='5f221fb9-66f4-442a-92c9-d97ed5989cc7' SELECT [w].[Id], [w].[AmmunitionType], [w].[IsAutomatic], [w].[Name], [w].[OwnerFullName], [w].[SynergyWithId] FROM [Weapons] AS [w] WHERE CASE - WHEN [w].[AmmunitionType] IN ( - SELECT [a].[value] - FROM OPENJSON(@ammoTypes) WITH ([value] int '$') AS [a] - ) THEN @key + WHEN [w].[AmmunitionType] = @ammoTypes1 THEN @key ELSE @key -END IN ( - SELECT [k].[value] - FROM OPENJSON(@keys) WITH ([value] uniqueidentifier '$') AS [k] -) +END IN (@keys1, @keys2) """); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/TPTGearsOfWarQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/TPTGearsOfWarQuerySqlServerTest.cs index d46673bd593..a7dbbebe847 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/TPTGearsOfWarQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/TPTGearsOfWarQuerySqlServerTest.cs @@ -297,7 +297,12 @@ FROM [Tags] AS [t] """, // """ -@tags='["34c8d86e-a4ac-4be5-827f-584dda348a07","df36f493-463f-4123-83f9-6b135deeb7ba","a8ad98f9-e023-4e2a-9a70-c2728455bd34","70534e05-782c-4052-8720-c2c54481ce5f","a7be028a-0cf2-448f-ab55-ce8bc5d8cf69","b39a6fba-9026-4d69-828e-fd7068673e57"]' (Size = 4000) +@tags1='34c8d86e-a4ac-4be5-827f-584dda348a07' +@tags2='df36f493-463f-4123-83f9-6b135deeb7ba' +@tags3='a8ad98f9-e023-4e2a-9a70-c2728455bd34' +@tags4='70534e05-782c-4052-8720-c2c54481ce5f' +@tags5='a7be028a-0cf2-448f-ab55-ce8bc5d8cf69' +@tags6='b39a6fba-9026-4d69-828e-fd7068673e57' SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank], CASE WHEN [o].[Nickname] IS NOT NULL THEN N'Officer' @@ -305,10 +310,7 @@ WHEN [o].[Nickname] IS NOT NULL THEN N'Officer' FROM [Gears] AS [g] LEFT JOIN [Officers] AS [o] ON [g].[Nickname] = [o].[Nickname] AND [g].[SquadId] = [o].[SquadId] LEFT JOIN [Tags] AS [t] ON [g].[Nickname] = [t].[GearNickName] AND [g].[SquadId] = [t].[GearSquadId] -WHERE [t].[Id] IS NOT NULL AND [t].[Id] IN ( - SELECT [t0].[value] - FROM OPENJSON(@tags) WITH ([value] uniqueidentifier '$') AS [t0] -) +WHERE [t].[Id] IS NOT NULL AND [t].[Id] IN (@tags1, @tags2, @tags3, @tags4, @tags5, @tags6) """); } @@ -323,7 +325,12 @@ FROM [Tags] AS [t] """, // """ -@tags='["34c8d86e-a4ac-4be5-827f-584dda348a07","df36f493-463f-4123-83f9-6b135deeb7ba","a8ad98f9-e023-4e2a-9a70-c2728455bd34","70534e05-782c-4052-8720-c2c54481ce5f","a7be028a-0cf2-448f-ab55-ce8bc5d8cf69","b39a6fba-9026-4d69-828e-fd7068673e57"]' (Size = 4000) +@tags1='34c8d86e-a4ac-4be5-827f-584dda348a07' +@tags2='df36f493-463f-4123-83f9-6b135deeb7ba' +@tags3='a8ad98f9-e023-4e2a-9a70-c2728455bd34' +@tags4='70534e05-782c-4052-8720-c2c54481ce5f' +@tags5='a7be028a-0cf2-448f-ab55-ce8bc5d8cf69' +@tags6='b39a6fba-9026-4d69-828e-fd7068673e57' SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank], CASE WHEN [o].[Nickname] IS NOT NULL THEN N'Officer' @@ -332,10 +339,7 @@ FROM [Gears] AS [g] LEFT JOIN [Officers] AS [o] ON [g].[Nickname] = [o].[Nickname] AND [g].[SquadId] = [o].[SquadId] INNER JOIN [Cities] AS [c] ON [g].[CityOfBirthName] = [c].[Name] LEFT JOIN [Tags] AS [t] ON [g].[Nickname] = [t].[GearNickName] AND [g].[SquadId] = [t].[GearSquadId] -WHERE [c].[Location] IS NOT NULL AND [t].[Id] IN ( - SELECT [t0].[value] - FROM OPENJSON(@tags) WITH ([value] uniqueidentifier '$') AS [t0] -) +WHERE [c].[Location] IS NOT NULL AND [t].[Id] IN (@tags1, @tags2, @tags3, @tags4, @tags5, @tags6) """); } @@ -350,7 +354,12 @@ FROM [Tags] AS [t] """, // """ -@tags='["34c8d86e-a4ac-4be5-827f-584dda348a07","df36f493-463f-4123-83f9-6b135deeb7ba","a8ad98f9-e023-4e2a-9a70-c2728455bd34","70534e05-782c-4052-8720-c2c54481ce5f","a7be028a-0cf2-448f-ab55-ce8bc5d8cf69","b39a6fba-9026-4d69-828e-fd7068673e57"]' (Size = 4000) +@tags1='34c8d86e-a4ac-4be5-827f-584dda348a07' +@tags2='df36f493-463f-4123-83f9-6b135deeb7ba' +@tags3='a8ad98f9-e023-4e2a-9a70-c2728455bd34' +@tags4='70534e05-782c-4052-8720-c2c54481ce5f' +@tags5='a7be028a-0cf2-448f-ab55-ce8bc5d8cf69' +@tags6='b39a6fba-9026-4d69-828e-fd7068673e57' SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank], CASE WHEN [o].[Nickname] IS NOT NULL THEN N'Officer' @@ -358,10 +367,7 @@ END AS [Discriminator] FROM [Gears] AS [g] LEFT JOIN [Officers] AS [o] ON [g].[Nickname] = [o].[Nickname] AND [g].[SquadId] = [o].[SquadId] LEFT JOIN [Tags] AS [t] ON [g].[Nickname] = [t].[GearNickName] AND [g].[SquadId] = [t].[GearSquadId] -WHERE [t].[Id] IS NOT NULL AND [t].[Id] IN ( - SELECT [t0].[value] - FROM OPENJSON(@tags) WITH ([value] uniqueidentifier '$') AS [t0] -) +WHERE [t].[Id] IS NOT NULL AND [t].[Id] IN (@tags1, @tags2, @tags3, @tags4, @tags5, @tags6) """); } @@ -2061,14 +2067,12 @@ public override async Task Non_unicode_string_literals_in_contains_is_used_for_n AssertSql( """ -@cities='["Unknown","Jacinto\u0027s location","Ephyra\u0027s location"]' (Size = 4000) +@cities1='Unknown' (Size = 100) (DbType = AnsiString) +@cities2='Jacinto's location' (Size = 100) (DbType = AnsiString), @cities3='Ephyra's location' (Size = 100) (DbType = AnsiString) SELECT [c].[Name], [c].[Location], [c].[Nation] FROM [Cities] AS [c] -WHERE [c].[Location] IN ( - SELECT [c0].[value] - FROM OPENJSON(@cities) WITH ([value] varchar(100) '$') AS [c0] -) +WHERE [c].[Location] IN (@cities1, @cities2, @cities3) """); } @@ -2936,14 +2940,13 @@ public override async Task Contains_with_local_nullable_guid_list_closure(bool a AssertSql( """ -@ids='["df36f493-463f-4123-83f9-6b135deeb7ba","23cbcf9b-ce14-45cf-aafa-2c2667ebfdd3","ab1b82d7-88db-42bd-a132-7eef9aa68af4"]' (Size = 4000) +@ids1='df36f493-463f-4123-83f9-6b135deeb7ba' +@ids2='23cbcf9b-ce14-45cf-aafa-2c2667ebfdd3' +@ids3='ab1b82d7-88db-42bd-a132-7eef9aa68af4' SELECT [t].[Id], [t].[GearNickName], [t].[GearSquadId], [t].[IssueDate], [t].[Note] FROM [Tags] AS [t] -WHERE [t].[Id] IN ( - SELECT [i].[value] - FROM OPENJSON(@ids) WITH ([value] uniqueidentifier '$') AS [i] -) +WHERE [t].[Id] IN (@ids1, @ids2, @ids3) """); } @@ -3521,7 +3524,7 @@ public override async Task Contains_on_nullable_array_produces_correct_sql(bool AssertSql( """ -@cities_without_nulls='["Ephyra"]' (Size = 4000) +@cities1='Ephyra' (Size = 450) SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank], CASE WHEN [o].[Nickname] IS NOT NULL THEN N'Officer' @@ -3529,10 +3532,7 @@ END AS [Discriminator] FROM [Gears] AS [g] LEFT JOIN [Officers] AS [o] ON [g].[Nickname] = [o].[Nickname] AND [g].[SquadId] = [o].[SquadId] LEFT JOIN [Cities] AS [c] ON [g].[AssignedCityName] = [c].[Name] -WHERE [g].[SquadId] < 2 AND ([c].[Name] IN ( - SELECT [c0].[value] - FROM OPENJSON(@cities_without_nulls) AS [c0] -) OR [c].[Name] IS NULL) +WHERE [g].[SquadId] < 2 AND ([c].[Name] IS NULL OR [c].[Name] = @cities1) """); } @@ -6243,18 +6243,10 @@ public override async Task Correlated_collection_with_complex_order_by_funcletiz AssertSql( """ -@nicknames='[]' (Size = 4000) - SELECT [g].[Nickname], [g].[SquadId], [w].[Name], [w].[Id] FROM [Gears] AS [g] LEFT JOIN [Weapons] AS [w] ON [g].[FullName] = [w].[OwnerFullName] -ORDER BY CASE - WHEN [g].[Nickname] IN ( - SELECT [n].[value] - FROM OPENJSON(@nicknames) WITH ([value] nvarchar(450) '$') AS [n] - ) THEN CAST(1 AS bit) - ELSE CAST(0 AS bit) -END DESC, [g].[Nickname], [g].[SquadId] +ORDER BY [g].[Nickname], [g].[SquadId] """); } @@ -6984,14 +6976,11 @@ public override async Task DateTimeOffset_Contains_Less_than_Greater_than(bool a """ @start='1902-01-01T10:00:00.1234567+01:30' @end='1902-01-03T10:00:00.1234567+01:30' -@dates='["1902-01-02T10:00:00.1234567+01:30"]' (Size = 4000) +@dates1='1902-01-02T10:00:00.1234567+01:30' SELECT [m].[Id], [m].[CodeName], [m].[Date], [m].[Difficulty], [m].[Duration], [m].[Rating], [m].[Time], [m].[Timeline] FROM [Missions] AS [m] -WHERE @start <= CAST(CONVERT(date, [m].[Timeline]) AS datetimeoffset) AND [m].[Timeline] < @end AND [m].[Timeline] IN ( - SELECT [d].[value] - FROM OPENJSON(@dates) WITH ([value] datetimeoffset '$') AS [d] -) +WHERE @start <= CAST(CONVERT(date, [m].[Timeline]) AS datetimeoffset) AND [m].[Timeline] < @end AND [m].[Timeline] = @dates1 """); } @@ -7782,20 +7771,11 @@ public override async Task OrderBy_Contains_empty_list(bool async) AssertSql( """ -@ids='[]' (Size = 4000) - SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank], CASE WHEN [o].[Nickname] IS NOT NULL THEN N'Officer' END AS [Discriminator] FROM [Gears] AS [g] LEFT JOIN [Officers] AS [o] ON [g].[Nickname] = [o].[Nickname] AND [g].[SquadId] = [o].[SquadId] -ORDER BY CASE - WHEN [g].[SquadId] IN ( - SELECT [i].[value] - FROM OPENJSON(@ids) WITH ([value] int '$') AS [i] - ) THEN CAST(1 AS bit) - ELSE CAST(0 AS bit) -END """); } @@ -8418,15 +8398,12 @@ public override async Task Enum_array_contains(bool async) AssertSql( """ -@types_without_nulls='[1]' (Size = 4000) +@types2='1' SELECT [w].[Id], [w].[AmmunitionType], [w].[IsAutomatic], [w].[Name], [w].[OwnerFullName], [w].[SynergyWithId] FROM [Weapons] AS [w] LEFT JOIN [Weapons] AS [w0] ON [w].[SynergyWithId] = [w0].[Id] -WHERE [w0].[Id] IS NOT NULL AND ([w0].[AmmunitionType] IN ( - SELECT [t].[value] - FROM OPENJSON(@types_without_nulls) AS [t] -) OR [w0].[AmmunitionType] IS NULL) +WHERE [w0].[Id] IS NOT NULL AND ([w0].[AmmunitionType] IS NULL OR [w0].[AmmunitionType] = @types2) """); } @@ -9171,17 +9148,15 @@ public override async Task Where_bool_column_and_Contains(bool async) AssertSql( """ -@values='[false,true]' (Size = 4000) +@values1='False' +@values2='True' SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank], CASE WHEN [o].[Nickname] IS NOT NULL THEN N'Officer' END AS [Discriminator] FROM [Gears] AS [g] LEFT JOIN [Officers] AS [o] ON [g].[Nickname] = [o].[Nickname] AND [g].[SquadId] = [o].[SquadId] -WHERE [g].[HasSoulPatch] = CAST(1 AS bit) AND [g].[HasSoulPatch] IN ( - SELECT [v].[value] - FROM OPENJSON(@values) WITH ([value] bit '$') AS [v] -) +WHERE [g].[HasSoulPatch] = CAST(1 AS bit) AND [g].[HasSoulPatch] IN (@values1, @values2) """); } @@ -9191,17 +9166,15 @@ public override async Task Where_bool_column_or_Contains(bool async) AssertSql( """ -@values='[false,true]' (Size = 4000) +@values1='False' +@values2='True' SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank], CASE WHEN [o].[Nickname] IS NOT NULL THEN N'Officer' END AS [Discriminator] FROM [Gears] AS [g] LEFT JOIN [Officers] AS [o] ON [g].[Nickname] = [o].[Nickname] AND [g].[SquadId] = [o].[SquadId] -WHERE [g].[HasSoulPatch] = CAST(1 AS bit) AND [g].[HasSoulPatch] IN ( - SELECT [v].[value] - FROM OPENJSON(@values) WITH ([value] bit '$') AS [v] -) +WHERE [g].[HasSoulPatch] = CAST(1 AS bit) AND [g].[HasSoulPatch] IN (@values1, @values2) """); } @@ -10368,7 +10341,8 @@ public override async Task Nav_expansion_inside_Contains_argument(bool async) AssertSql( """ -@numbers='[1,-1]' (Size = 4000) +@numbers1='1' +@numbers2='-1' SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank], CASE WHEN [o].[Nickname] IS NOT NULL THEN N'Officer' @@ -10381,10 +10355,7 @@ SELECT 1 FROM [Weapons] AS [w] WHERE [g].[FullName] = [w].[OwnerFullName]) THEN 1 ELSE 0 -END IN ( - SELECT [n].[value] - FROM OPENJSON(@numbers) WITH ([value] int '$') AS [n] -) +END IN (@numbers1, @numbers2) """); } @@ -10394,7 +10365,7 @@ public override async Task Nav_expansion_with_member_pushdown_inside_Contains_ar AssertSql( """ -@weapons='["Marcus\u0027 Lancer","Dom\u0027s Gnasher"]' (Size = 4000) +@weapons1='Marcus' Lancer' (Size = 4000), @weapons2='Dom's Gnasher' (Size = 4000) SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank], CASE WHEN [o].[Nickname] IS NOT NULL THEN N'Officer' @@ -10405,10 +10376,7 @@ FROM [Gears] AS [g] SELECT TOP(1) [w0].[Name] FROM [Weapons] AS [w0] WHERE [g].[FullName] = [w0].[OwnerFullName] - ORDER BY [w0].[Id]) IN ( - SELECT [w].[value] - FROM OPENJSON(@weapons) WITH ([value] nvarchar(max) '$') AS [w] -) + ORDER BY [w0].[Id]) IN (@weapons1, @weapons2) """); } @@ -10418,7 +10386,9 @@ public override async Task Subquery_inside_Take_argument(bool async) AssertSql( """ -@numbers='[0,1,2]' (Size = 4000) +@numbers1='0' +@numbers2='1' +@numbers3='2' SELECT [g].[Nickname], [g].[SquadId], [w1].[Id], [w1].[AmmunitionType], [w1].[IsAutomatic], [w1].[Name], [w1].[OwnerFullName], [w1].[SynergyWithId] FROM [Gears] AS [g] @@ -10429,9 +10399,9 @@ LEFT JOIN ( FROM [Weapons] AS [w] ) AS [w0] WHERE [w0].[row] <= ISNULL(( - SELECT [n].[value] - FROM OPENJSON(@numbers) WITH ([value] int '$') AS [n] - ORDER BY [n].[value] + SELECT [n].[Value] + FROM (VALUES (1, @numbers1), (2, @numbers2), (3, @numbers3)) AS [n]([_ord], [Value]) + ORDER BY [n].[Value] OFFSET 1 ROWS FETCH NEXT 1 ROWS ONLY), 0) ) AS [w1] ON [g].[FullName] = [w1].[OwnerFullName] ORDER BY [g].[Nickname], [g].[SquadId], [w1].[OwnerFullName], [w1].[Id] @@ -10655,9 +10625,10 @@ public override async Task Nested_contains_with_enum(bool async) AssertSql( """ -@ranks='[1]' (Size = 4000) +@ranks1='1' @key='5f221fb9-66f4-442a-92c9-d97ed5989cc7' -@keys='["0a47bcb7-a1cb-4345-8944-c58f82d6aac7","5f221fb9-66f4-442a-92c9-d97ed5989cc7"]' (Size = 4000) +@keys1='0a47bcb7-a1cb-4345-8944-c58f82d6aac7' +@keys2='5f221fb9-66f4-442a-92c9-d97ed5989cc7' SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank], CASE WHEN [o].[Nickname] IS NOT NULL THEN N'Officer' @@ -10665,34 +10636,23 @@ END AS [Discriminator] FROM [Gears] AS [g] LEFT JOIN [Officers] AS [o] ON [g].[Nickname] = [o].[Nickname] AND [g].[SquadId] = [o].[SquadId] WHERE CASE - WHEN [g].[Rank] IN ( - SELECT [r].[value] - FROM OPENJSON(@ranks) WITH ([value] int '$') AS [r] - ) THEN @key + WHEN [g].[Rank] = @ranks1 THEN @key ELSE @key -END IN ( - SELECT [k].[value] - FROM OPENJSON(@keys) WITH ([value] uniqueidentifier '$') AS [k] -) +END IN (@keys1, @keys2) """, // """ -@ammoTypes='[1]' (Size = 4000) +@ammoTypes1='1' @key='5f221fb9-66f4-442a-92c9-d97ed5989cc7' -@keys='["0a47bcb7-a1cb-4345-8944-c58f82d6aac7","5f221fb9-66f4-442a-92c9-d97ed5989cc7"]' (Size = 4000) +@keys1='0a47bcb7-a1cb-4345-8944-c58f82d6aac7' +@keys2='5f221fb9-66f4-442a-92c9-d97ed5989cc7' SELECT [w].[Id], [w].[AmmunitionType], [w].[IsAutomatic], [w].[Name], [w].[OwnerFullName], [w].[SynergyWithId] FROM [Weapons] AS [w] WHERE CASE - WHEN [w].[AmmunitionType] IN ( - SELECT [a].[value] - FROM OPENJSON(@ammoTypes) WITH ([value] int '$') AS [a] - ) THEN @key + WHEN [w].[AmmunitionType] = @ammoTypes1 THEN @key ELSE @key -END IN ( - SELECT [k].[value] - FROM OPENJSON(@keys) WITH ([value] uniqueidentifier '$') AS [k] -) +END IN (@keys1, @keys2) """); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/TemporalComplexNavigationsCollectionsQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/TemporalComplexNavigationsCollectionsQuerySqlServerTest.cs index a3f0a818f60..ddfd414f21e 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/TemporalComplexNavigationsCollectionsQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/TemporalComplexNavigationsCollectionsQuerySqlServerTest.cs @@ -2303,25 +2303,20 @@ public override async Task Collection_projection_over_GroupBy_over_parameter(boo AssertSql( """ -@validIds='["L1 01","L1 02"]' (Size = 4000) +@validIds1='L1 01' (Size = 4000) +@validIds2='L1 02' (Size = 4000) SELECT [l1].[Date], [l2].[Id] FROM ( SELECT [l].[Date] FROM [LevelOne] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [l] - WHERE [l].[Name] IN ( - SELECT [v].[value] - FROM OPENJSON(@validIds) WITH ([value] nvarchar(max) '$') AS [v] - ) + WHERE [l].[Name] IN (@validIds1, @validIds2) GROUP BY [l].[Date] ) AS [l1] LEFT JOIN ( SELECT [l0].[Id], [l0].[Date] FROM [LevelOne] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [l0] - WHERE [l0].[Name] IN ( - SELECT [v0].[value] - FROM OPENJSON(@validIds) WITH ([value] nvarchar(max) '$') AS [v0] - ) + WHERE [l0].[Name] IN (@validIds1, @validIds2) ) AS [l2] ON [l1].[Date] = [l2].[Date] ORDER BY [l1].[Date] """); @@ -2620,7 +2615,8 @@ public override async Task LeftJoin_with_Any_on_outer_source_and_projecting_coll AssertSql( """ -@validIds='["L1 01","L1 02"]' (Size = 4000) +@validIds1='L1 01' (Size = 4000) +@validIds2='L1 02' (Size = 4000) SELECT CASE WHEN [l0].[Id] IS NULL THEN 0 @@ -2629,10 +2625,7 @@ ELSE [l0].[Id] FROM [LevelOne] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [l] LEFT JOIN [LevelTwo] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [l0] ON [l].[Id] = [l0].[Level1_Required_Id] LEFT JOIN [LevelThree] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [l1] ON [l0].[Id] = [l1].[OneToMany_Required_Inverse3Id] -WHERE [l].[Name] IN ( - SELECT [v].[value] - FROM OPENJSON(@validIds) WITH ([value] nvarchar(max) '$') AS [v] -) +WHERE [l].[Name] IN (@validIds1, @validIds2) ORDER BY [l].[Id], [l0].[Id] """); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/TemporalComplexNavigationsCollectionsSharedTypeQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/TemporalComplexNavigationsCollectionsSharedTypeQuerySqlServerTest.cs index a7a4200801c..f55c0b26dc0 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/TemporalComplexNavigationsCollectionsSharedTypeQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/TemporalComplexNavigationsCollectionsSharedTypeQuerySqlServerTest.cs @@ -1844,7 +1844,8 @@ public override async Task LeftJoin_with_Any_on_outer_source_and_projecting_coll AssertSql( """ -@validIds='["L1 01","L1 02"]' (Size = 4000) +@validIds1='L1 01' (Size = 4000) +@validIds2='L1 02' (Size = 4000) SELECT CASE WHEN [s].[OneToOne_Required_PK_Date] IS NULL OR [s].[Level1_Required_Id] IS NULL OR [s].[OneToMany_Required_Inverse2Id] IS NULL OR CASE @@ -1878,10 +1879,7 @@ WHERE [l3].[Level2_Required_Id] IS NOT NULL AND [l3].[OneToMany_Required_Inverse ) AS [l4] ON CASE WHEN [s].[OneToOne_Required_PK_Date] IS NOT NULL AND [s].[Level1_Required_Id] IS NOT NULL AND [s].[OneToMany_Required_Inverse2Id] IS NOT NULL AND [s].[PeriodEnd0] IS NOT NULL AND [s].[PeriodStart0] IS NOT NULL THEN [s].[Id0] END = [l4].[OneToMany_Required_Inverse3Id] -WHERE [l].[Name] IN ( - SELECT [v].[value] - FROM OPENJSON(@validIds) WITH ([value] nvarchar(max) '$') AS [v] -) +WHERE [l].[Name] IN (@validIds1, @validIds2) ORDER BY [l].[Id], [s].[Id], [s].[Id0] """); } @@ -3032,25 +3030,20 @@ public override async Task Collection_projection_over_GroupBy_over_parameter(boo AssertSql( """ -@validIds='["L1 01","L1 02"]' (Size = 4000) +@validIds1='L1 01' (Size = 4000) +@validIds2='L1 02' (Size = 4000) SELECT [l1].[Date], [l2].[Id] FROM ( SELECT [l].[Date] FROM [Level1] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [l] - WHERE [l].[Name] IN ( - SELECT [v].[value] - FROM OPENJSON(@validIds) WITH ([value] nvarchar(max) '$') AS [v] - ) + WHERE [l].[Name] IN (@validIds1, @validIds2) GROUP BY [l].[Date] ) AS [l1] LEFT JOIN ( SELECT [l0].[Id], [l0].[Date] FROM [Level1] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [l0] - WHERE [l0].[Name] IN ( - SELECT [v0].[value] - FROM OPENJSON(@validIds) WITH ([value] nvarchar(max) '$') AS [v0] - ) + WHERE [l0].[Name] IN (@validIds1, @validIds2) ) AS [l2] ON [l1].[Date] = [l2].[Date] ORDER BY [l1].[Date] """); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/TemporalGearsOfWarQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/TemporalGearsOfWarQuerySqlServerTest.cs index 4b86beb08b0..177475210b3 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/TemporalGearsOfWarQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/TemporalGearsOfWarQuerySqlServerTest.cs @@ -72,15 +72,10 @@ FROM [Tags] AS [t] """, // """ -@tags='[]' (Size = 4000) - SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[PeriodEnd], [g].[PeriodStart], [g].[Rank], [t].[Id], [t].[GearNickName], [t].[GearSquadId], [t].[IssueDate], [t].[Note], [t].[PeriodEnd], [t].[PeriodStart] FROM [Gears] AS [g] LEFT JOIN [Tags] AS [t] ON [g].[Nickname] = [t].[GearNickName] AND [g].[SquadId] = [t].[GearSquadId] -WHERE [t].[Id] IS NOT NULL AND [t].[Id] IN ( - SELECT [t0].[value] - FROM OPENJSON(@tags) WITH ([value] uniqueidentifier '$') AS [t0] -) +WHERE 0 = 1 """); } @@ -96,16 +91,11 @@ FROM [Tags] AS [t] """, // """ -@tags='[]' (Size = 4000) - SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[PeriodEnd], [g].[PeriodStart], [g].[Rank], [t].[Id], [t].[GearNickName], [t].[GearSquadId], [t].[IssueDate], [t].[Note], [t].[PeriodEnd], [t].[PeriodStart] FROM [Gears] AS [g] INNER JOIN [Cities] AS [c] ON [g].[CityOfBirthName] = [c].[Name] LEFT JOIN [Tags] AS [t] ON [g].[Nickname] = [t].[GearNickName] AND [g].[SquadId] = [t].[GearSquadId] -WHERE [c].[Location] IS NOT NULL AND [t].[Id] IN ( - SELECT [t0].[value] - FROM OPENJSON(@tags) WITH ([value] uniqueidentifier '$') AS [t0] -) +WHERE 0 = 1 """); } @@ -121,15 +111,10 @@ FROM [Tags] AS [t] """, // """ -@tags='[]' (Size = 4000) - SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[PeriodEnd], [g].[PeriodStart], [g].[Rank] FROM [Gears] AS [g] LEFT JOIN [Tags] AS [t] ON [g].[Nickname] = [t].[GearNickName] AND [g].[SquadId] = [t].[GearSquadId] -WHERE [t].[Id] IS NOT NULL AND [t].[Id] IN ( - SELECT [t0].[value] - FROM OPENJSON(@tags) WITH ([value] uniqueidentifier '$') AS [t0] -) +WHERE 0 = 1 """); } @@ -1305,14 +1290,12 @@ public override async Task Where_bool_column_or_Contains(bool async) AssertSql( """ -@values='[false,true]' (Size = 4000) +@values1='False' +@values2='True' SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[PeriodEnd], [g].[PeriodStart], [g].[Rank] FROM [Gears] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [g] -WHERE [g].[HasSoulPatch] = CAST(1 AS bit) AND [g].[HasSoulPatch] IN ( - SELECT [v].[value] - FROM OPENJSON(@values) WITH ([value] bit '$') AS [v] -) +WHERE [g].[HasSoulPatch] = CAST(1 AS bit) AND [g].[HasSoulPatch] IN (@values1, @values2) """); } @@ -1412,15 +1395,12 @@ public override async Task Contains_on_nullable_array_produces_correct_sql(bool AssertSql( """ -@cities_without_nulls='["Ephyra"]' (Size = 4000) +@cities1='Ephyra' (Size = 450) SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[PeriodEnd], [g].[PeriodStart], [g].[Rank] FROM [Gears] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [g] LEFT JOIN [Cities] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [c] ON [g].[AssignedCityName] = [c].[Name] -WHERE [g].[SquadId] < 2 AND ([c].[Name] IN ( - SELECT [c0].[value] - FROM OPENJSON(@cities_without_nulls) AS [c0] -) OR [c].[Name] IS NULL) +WHERE [g].[SquadId] < 2 AND ([c].[Name] IS NULL OR [c].[Name] = @cities1) """); } @@ -4964,15 +4944,12 @@ public override async Task Enum_array_contains(bool async) AssertSql( """ -@types_without_nulls='[1]' (Size = 4000) +@types2='1' SELECT [w].[Id], [w].[AmmunitionType], [w].[IsAutomatic], [w].[Name], [w].[OwnerFullName], [w].[PeriodEnd], [w].[PeriodStart], [w].[SynergyWithId] FROM [Weapons] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [w] LEFT JOIN [Weapons] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [w0] ON [w].[SynergyWithId] = [w0].[Id] -WHERE [w0].[Id] IS NOT NULL AND ([w0].[AmmunitionType] IN ( - SELECT [t].[value] - FROM OPENJSON(@types_without_nulls) AS [t] -) OR [w0].[AmmunitionType] IS NULL) +WHERE [w0].[Id] IS NOT NULL AND ([w0].[AmmunitionType] IS NULL OR [w0].[AmmunitionType] = @types2) """); } @@ -5487,17 +5464,8 @@ public override async Task OrderBy_Contains_empty_list(bool async) AssertSql( """ -@ids='[]' (Size = 4000) - SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[PeriodEnd], [g].[PeriodStart], [g].[Rank] FROM [Gears] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [g] -ORDER BY CASE - WHEN [g].[SquadId] IN ( - SELECT [i].[value] - FROM OPENJSON(@ids) WITH ([value] int '$') AS [i] - ) THEN CAST(1 AS bit) - ELSE CAST(0 AS bit) -END """); } @@ -5597,14 +5565,11 @@ public override async Task DateTimeOffset_Contains_Less_than_Greater_than(bool a """ @start='1902-01-01T10:00:00.1234567+01:30' @end='1902-01-03T10:00:00.1234567+01:30' -@dates='["1902-01-02T10:00:00.1234567+01:30"]' (Size = 4000) +@dates1='1902-01-02T10:00:00.1234567+01:30' SELECT [m].[Id], [m].[BriefingDocument], [m].[BriefingDocumentFileExtension], [m].[CodeName], [m].[Date], [m].[Difficulty], [m].[Duration], [m].[PeriodEnd], [m].[PeriodStart], [m].[Rating], [m].[Time], [m].[Timeline] FROM [Missions] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [m] -WHERE @start <= CAST(CONVERT(date, [m].[Timeline]) AS datetimeoffset) AND [m].[Timeline] < @end AND [m].[Timeline] IN ( - SELECT [d].[value] - FROM OPENJSON(@dates) WITH ([value] datetimeoffset '$') AS [d] -) +WHERE @start <= CAST(CONVERT(date, [m].[Timeline]) AS datetimeoffset) AND [m].[Timeline] < @end AND [m].[Timeline] = @dates1 """); } @@ -5956,14 +5921,12 @@ public override async Task Non_unicode_string_literals_in_contains_is_used_for_n AssertSql( """ -@cities='["Unknown","Jacinto\u0027s location","Ephyra\u0027s location"]' (Size = 4000) +@cities1='Unknown' (Size = 100) (DbType = AnsiString) +@cities2='Jacinto's location' (Size = 100) (DbType = AnsiString), @cities3='Ephyra's location' (Size = 100) (DbType = AnsiString) SELECT [c].[Name], [c].[Location], [c].[Nation], [c].[PeriodEnd], [c].[PeriodStart] FROM [Cities] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [c] -WHERE [c].[Location] IN ( - SELECT [c0].[value] - FROM OPENJSON(@cities) WITH ([value] varchar(100) '$') AS [c0] -) +WHERE [c].[Location] IN (@cities1, @cities2, @cities3) """); } @@ -7186,18 +7149,10 @@ public override async Task Correlated_collection_with_complex_order_by_funcletiz AssertSql( """ -@nicknames='[]' (Size = 4000) - SELECT [g].[Nickname], [g].[SquadId], [w].[Name], [w].[Id] FROM [Gears] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [g] LEFT JOIN [Weapons] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [w] ON [g].[FullName] = [w].[OwnerFullName] -ORDER BY CASE - WHEN [g].[Nickname] IN ( - SELECT [n].[value] - FROM OPENJSON(@nicknames) WITH ([value] nvarchar(450) '$') AS [n] - ) THEN CAST(1 AS bit) - ELSE CAST(0 AS bit) -END DESC, [g].[Nickname], [g].[SquadId] +ORDER BY [g].[Nickname], [g].[SquadId] """); } @@ -7476,14 +7431,13 @@ public override async Task Contains_with_local_nullable_guid_list_closure(bool a AssertSql( """ -@ids='["df36f493-463f-4123-83f9-6b135deeb7ba","23cbcf9b-ce14-45cf-aafa-2c2667ebfdd3","ab1b82d7-88db-42bd-a132-7eef9aa68af4"]' (Size = 4000) +@ids1='df36f493-463f-4123-83f9-6b135deeb7ba' +@ids2='23cbcf9b-ce14-45cf-aafa-2c2667ebfdd3' +@ids3='ab1b82d7-88db-42bd-a132-7eef9aa68af4' SELECT [t].[Id], [t].[GearNickName], [t].[GearSquadId], [t].[IssueDate], [t].[Note], [t].[PeriodEnd], [t].[PeriodStart] FROM [Tags] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [t] -WHERE [t].[Id] IN ( - SELECT [i].[value] - FROM OPENJSON(@ids) WITH ([value] uniqueidentifier '$') AS [i] -) +WHERE [t].[Id] IN (@ids1, @ids2, @ids3) """); } @@ -8088,14 +8042,12 @@ public override async Task Where_bool_column_and_Contains(bool async) AssertSql( """ -@values='[false,true]' (Size = 4000) +@values1='False' +@values2='True' SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[PeriodEnd], [g].[PeriodStart], [g].[Rank] FROM [Gears] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [g] -WHERE [g].[HasSoulPatch] = CAST(1 AS bit) AND [g].[HasSoulPatch] IN ( - SELECT [v].[value] - FROM OPENJSON(@values) WITH ([value] bit '$') AS [v] -) +WHERE [g].[HasSoulPatch] = CAST(1 AS bit) AND [g].[HasSoulPatch] IN (@values1, @values2) """); } @@ -9081,7 +9033,8 @@ public override async Task Nav_expansion_inside_Contains_argument(bool async) AssertSql( """ -@numbers='[1,-1]' (Size = 4000) +@numbers1='1' +@numbers2='-1' SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[PeriodEnd], [g].[PeriodStart], [g].[Rank] FROM [Gears] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [g] @@ -9091,10 +9044,7 @@ SELECT 1 FROM [Weapons] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [w] WHERE [g].[FullName] = [w].[OwnerFullName]) THEN 1 ELSE 0 -END IN ( - SELECT [n].[value] - FROM OPENJSON(@numbers) WITH ([value] int '$') AS [n] -) +END IN (@numbers1, @numbers2) """); } @@ -9104,7 +9054,7 @@ public override async Task Nav_expansion_with_member_pushdown_inside_Contains_ar AssertSql( """ -@weapons='["Marcus\u0027 Lancer","Dom\u0027s Gnasher"]' (Size = 4000) +@weapons1='Marcus' Lancer' (Size = 4000), @weapons2='Dom's Gnasher' (Size = 4000) SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[PeriodEnd], [g].[PeriodStart], [g].[Rank] FROM [Gears] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [g] @@ -9112,10 +9062,7 @@ public override async Task Nav_expansion_with_member_pushdown_inside_Contains_ar SELECT TOP(1) [w0].[Name] FROM [Weapons] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [w0] WHERE [g].[FullName] = [w0].[OwnerFullName] - ORDER BY [w0].[Id]) IN ( - SELECT [w].[value] - FROM OPENJSON(@weapons) WITH ([value] nvarchar(max) '$') AS [w] -) + ORDER BY [w0].[Id]) IN (@weapons1, @weapons2) """); } @@ -9125,7 +9072,9 @@ public override async Task Subquery_inside_Take_argument(bool async) AssertSql( """ -@numbers='[0,1,2]' (Size = 4000) +@numbers1='0' +@numbers2='1' +@numbers3='2' SELECT [g].[Nickname], [g].[SquadId], [w1].[Id], [w1].[AmmunitionType], [w1].[IsAutomatic], [w1].[Name], [w1].[OwnerFullName], [w1].[PeriodEnd], [w1].[PeriodStart], [w1].[SynergyWithId] FROM [Gears] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [g] @@ -9136,9 +9085,9 @@ LEFT JOIN ( FROM [Weapons] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [w] ) AS [w0] WHERE [w0].[row] <= ISNULL(( - SELECT [n].[value] - FROM OPENJSON(@numbers) WITH ([value] int '$') AS [n] - ORDER BY [n].[value] + SELECT [n].[Value] + FROM (VALUES (1, @numbers1), (2, @numbers2), (3, @numbers3)) AS [n]([_ord], [Value]) + ORDER BY [n].[Value] OFFSET 1 ROWS FETCH NEXT 1 ROWS ONLY), 0) ) AS [w1] ON [g].[FullName] = [w1].[OwnerFullName] ORDER BY [g].[Nickname], [g].[SquadId], [w1].[OwnerFullName], [w1].[Id] @@ -9301,41 +9250,31 @@ public override async Task Nested_contains_with_enum(bool async) AssertSql( """ -@ranks='[1]' (Size = 4000) +@ranks1='1' @key='5f221fb9-66f4-442a-92c9-d97ed5989cc7' -@keys='["0a47bcb7-a1cb-4345-8944-c58f82d6aac7","5f221fb9-66f4-442a-92c9-d97ed5989cc7"]' (Size = 4000) +@keys1='0a47bcb7-a1cb-4345-8944-c58f82d6aac7' +@keys2='5f221fb9-66f4-442a-92c9-d97ed5989cc7' SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[PeriodEnd], [g].[PeriodStart], [g].[Rank] FROM [Gears] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [g] WHERE CASE - WHEN [g].[Rank] IN ( - SELECT [r].[value] - FROM OPENJSON(@ranks) WITH ([value] int '$') AS [r] - ) THEN @key + WHEN [g].[Rank] = @ranks1 THEN @key ELSE @key -END IN ( - SELECT [k].[value] - FROM OPENJSON(@keys) WITH ([value] uniqueidentifier '$') AS [k] -) +END IN (@keys1, @keys2) """, // """ -@ammoTypes='[1]' (Size = 4000) +@ammoTypes1='1' @key='5f221fb9-66f4-442a-92c9-d97ed5989cc7' -@keys='["0a47bcb7-a1cb-4345-8944-c58f82d6aac7","5f221fb9-66f4-442a-92c9-d97ed5989cc7"]' (Size = 4000) +@keys1='0a47bcb7-a1cb-4345-8944-c58f82d6aac7' +@keys2='5f221fb9-66f4-442a-92c9-d97ed5989cc7' SELECT [w].[Id], [w].[AmmunitionType], [w].[IsAutomatic], [w].[Name], [w].[OwnerFullName], [w].[PeriodEnd], [w].[PeriodStart], [w].[SynergyWithId] FROM [Weapons] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [w] WHERE CASE - WHEN [w].[AmmunitionType] IN ( - SELECT [a].[value] - FROM OPENJSON(@ammoTypes) WITH ([value] int '$') AS [a] - ) THEN @key + WHEN [w].[AmmunitionType] = @ammoTypes1 THEN @key ELSE @key -END IN ( - SELECT [k].[value] - FROM OPENJSON(@keys) WITH ([value] uniqueidentifier '$') AS [k] -) +END IN (@keys1, @keys2) """); } diff --git a/test/EFCore.SqlServer.HierarchyId.Tests/QueryTests.cs b/test/EFCore.SqlServer.HierarchyId.Tests/QueryTests.cs index 04a5f789787..34a97127b70 100644 --- a/test/EFCore.SqlServer.HierarchyId.Tests/QueryTests.cs +++ b/test/EFCore.SqlServer.HierarchyId.Tests/QueryTests.cs @@ -366,14 +366,12 @@ where ids.Contains(p.Id) Assert.Equal( """ -@ids='?' (Size = 4000) +@ids1='?' (DbType = Object) +@ids2='?' (DbType = Object) SELECT TOP(2) [p].[Name] FROM [Patriarchy] AS [p] -WHERE [p].[Id] IN ( - SELECT CAST([i].[value] AS hierarchyid) AS [value] - FROM OPENJSON(@ids) AS [i] -) +WHERE [p].[Id] IN (@ids1, @ids2) """, _db.Sql, ignoreLineEndingDifferences: true); diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/GearsOfWarQuerySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/GearsOfWarQuerySqliteTest.cs index 0af7969f17f..56e61425933 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/GearsOfWarQuerySqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/GearsOfWarQuerySqliteTest.cs @@ -401,16 +401,18 @@ public override async Task Include_where_list_contains_navigation2(bool async) """, // """ -@tags='["34C8D86E-A4AC-4BE5-827F-584DDA348A07","70534E05-782C-4052-8720-C2C54481CE5F","A7BE028A-0CF2-448F-AB55-CE8BC5D8CF69","A8AD98F9-E023-4E2A-9A70-C2728455BD34","B39A6FBA-9026-4D69-828E-FD7068673E57","DF36F493-463F-4123-83F9-6B135DEEB7BA"]' (Size = 235) +@tags1='34c8d86e-a4ac-4be5-827f-584dda348a07' +@tags2='70534e05-782c-4052-8720-c2c54481ce5f' +@tags3='a7be028a-0cf2-448f-ab55-ce8bc5d8cf69' +@tags4='a8ad98f9-e023-4e2a-9a70-c2728455bd34' +@tags5='b39a6fba-9026-4d69-828e-fd7068673e57' +@tags6='df36f493-463f-4123-83f9-6b135deeb7ba' SELECT "g"."Nickname", "g"."SquadId", "g"."AssignedCityName", "g"."CityOfBirthName", "g"."Discriminator", "g"."FullName", "g"."HasSoulPatch", "g"."LeaderNickname", "g"."LeaderSquadId", "g"."Rank", "t"."Id", "t"."GearNickName", "t"."GearSquadId", "t"."IssueDate", "t"."Note" FROM "Gears" AS "g" INNER JOIN "Cities" AS "c" ON "g"."CityOfBirthName" = "c"."Name" LEFT JOIN "Tags" AS "t" ON "g"."Nickname" = "t"."GearNickName" AND "g"."SquadId" = "t"."GearSquadId" -WHERE "c"."Location" IS NOT NULL AND "t"."Id" IN ( - SELECT "t0"."value" - FROM json_each(@tags) AS "t0" -) +WHERE "c"."Location" IS NOT NULL AND "t"."Id" IN (@tags1, @tags2, @tags3, @tags4, @tags5, @tags6) """); } @@ -756,14 +758,12 @@ public override async Task Non_unicode_string_literals_in_contains_is_used_for_n AssertSql( """ -@cities='["Unknown","Jacinto\u0027s location","Ephyra\u0027s location"]' (Size = 62) +@cities1='Unknown' (Size = 7) +@cities2='Jacinto's location' (Size = 18), @cities3='Ephyra's location' (Size = 17) SELECT "c"."Name", "c"."Location", "c"."Nation" FROM "Cities" AS "c" -WHERE "c"."Location" IN ( - SELECT "c0"."value" - FROM json_each(@cities) AS "c0" -) +WHERE "c"."Location" IN (@cities1, @cities2, @cities3) """); } @@ -1424,15 +1424,10 @@ public override async Task Correlated_collection_with_complex_order_by_funcletiz AssertSql( """ -@nicknames='[]' (Size = 2) - SELECT "g"."Nickname", "g"."SquadId", "w"."Name", "w"."Id" FROM "Gears" AS "g" LEFT JOIN "Weapons" AS "w" ON "g"."FullName" = "w"."OwnerFullName" -ORDER BY "g"."Nickname" IN ( - SELECT "n"."value" - FROM json_each(@nicknames) AS "n" -) DESC, "g"."Nickname", "g"."SquadId" +ORDER BY "g"."Nickname", "g"."SquadId" """); } @@ -1665,14 +1660,12 @@ public override async Task Where_bool_column_and_Contains(bool async) AssertSql( """ -@values='[false,true]' (Size = 12) +@values1='False' +@values2='True' SELECT "g"."Nickname", "g"."SquadId", "g"."AssignedCityName", "g"."CityOfBirthName", "g"."Discriminator", "g"."FullName", "g"."HasSoulPatch", "g"."LeaderNickname", "g"."LeaderSquadId", "g"."Rank" FROM "Gears" AS "g" -WHERE "g"."HasSoulPatch" AND "g"."HasSoulPatch" IN ( - SELECT "v"."value" - FROM json_each(@values) AS "v" -) +WHERE "g"."HasSoulPatch" AND "g"."HasSoulPatch" IN (@values1, @values2) """); } @@ -2196,15 +2189,12 @@ public override async Task Contains_on_nullable_array_produces_correct_sql(bool AssertSql( """ -@cities_without_nulls='["Ephyra"]' (Size = 10) +@cities1='Ephyra' (Size = 6) SELECT "g"."Nickname", "g"."SquadId", "g"."AssignedCityName", "g"."CityOfBirthName", "g"."Discriminator", "g"."FullName", "g"."HasSoulPatch", "g"."LeaderNickname", "g"."LeaderSquadId", "g"."Rank" FROM "Gears" AS "g" LEFT JOIN "Cities" AS "c" ON "g"."AssignedCityName" = "c"."Name" -WHERE "g"."SquadId" < 2 AND ("c"."Name" IN ( - SELECT "c0"."value" - FROM json_each(@cities_without_nulls) AS "c0" -) OR "c"."Name" IS NULL) +WHERE "g"."SquadId" < 2 AND ("c"."Name" IS NULL OR "c"."Name" = @cities1) """); } @@ -2456,15 +2446,12 @@ public override async Task Enum_array_contains(bool async) AssertSql( """ -@types_without_nulls='[1]' (Size = 3) +@types2='1' SELECT "w"."Id", "w"."AmmunitionType", "w"."IsAutomatic", "w"."Name", "w"."OwnerFullName", "w"."SynergyWithId" FROM "Weapons" AS "w" LEFT JOIN "Weapons" AS "w0" ON "w"."SynergyWithId" = "w0"."Id" -WHERE "w0"."Id" IS NOT NULL AND ("w0"."AmmunitionType" IN ( - SELECT "t"."value" - FROM json_each(@types_without_nulls) AS "t" -) OR "w0"."AmmunitionType" IS NULL) +WHERE "w0"."Id" IS NOT NULL AND ("w0"."AmmunitionType" IS NULL OR "w0"."AmmunitionType" = @types2) """); } @@ -3132,15 +3119,17 @@ public override async Task Navigation_accessed_twice_outside_and_inside_subquery """, // """ -@tags='["34C8D86E-A4AC-4BE5-827F-584DDA348A07","70534E05-782C-4052-8720-C2C54481CE5F","A7BE028A-0CF2-448F-AB55-CE8BC5D8CF69","A8AD98F9-E023-4E2A-9A70-C2728455BD34","B39A6FBA-9026-4D69-828E-FD7068673E57","DF36F493-463F-4123-83F9-6B135DEEB7BA"]' (Size = 235) +@tags1='34c8d86e-a4ac-4be5-827f-584dda348a07' +@tags2='70534e05-782c-4052-8720-c2c54481ce5f' +@tags3='a7be028a-0cf2-448f-ab55-ce8bc5d8cf69' +@tags4='a8ad98f9-e023-4e2a-9a70-c2728455bd34' +@tags5='b39a6fba-9026-4d69-828e-fd7068673e57' +@tags6='df36f493-463f-4123-83f9-6b135deeb7ba' SELECT "g"."Nickname", "g"."SquadId", "g"."AssignedCityName", "g"."CityOfBirthName", "g"."Discriminator", "g"."FullName", "g"."HasSoulPatch", "g"."LeaderNickname", "g"."LeaderSquadId", "g"."Rank" FROM "Gears" AS "g" LEFT JOIN "Tags" AS "t" ON "g"."Nickname" = "t"."GearNickName" AND "g"."SquadId" = "t"."GearSquadId" -WHERE "t"."Id" IS NOT NULL AND "t"."Id" IN ( - SELECT "t0"."value" - FROM json_each(@tags) AS "t0" -) +WHERE "t"."Id" IS NOT NULL AND "t"."Id" IN (@tags1, @tags2, @tags3, @tags4, @tags5, @tags6) """); } @@ -3875,14 +3864,12 @@ public override async Task Where_bool_column_or_Contains(bool async) AssertSql( """ -@values='[false,true]' (Size = 12) +@values1='False' +@values2='True' SELECT "g"."Nickname", "g"."SquadId", "g"."AssignedCityName", "g"."CityOfBirthName", "g"."Discriminator", "g"."FullName", "g"."HasSoulPatch", "g"."LeaderNickname", "g"."LeaderSquadId", "g"."Rank" FROM "Gears" AS "g" -WHERE "g"."HasSoulPatch" AND "g"."HasSoulPatch" IN ( - SELECT "v"."value" - FROM json_each(@values) AS "v" -) +WHERE "g"."HasSoulPatch" AND "g"."HasSoulPatch" IN (@values1, @values2) """); } @@ -4205,15 +4192,17 @@ public override async Task Include_where_list_contains_navigation(bool async) """, // """ -@tags='["34C8D86E-A4AC-4BE5-827F-584DDA348A07","70534E05-782C-4052-8720-C2C54481CE5F","A7BE028A-0CF2-448F-AB55-CE8BC5D8CF69","A8AD98F9-E023-4E2A-9A70-C2728455BD34","B39A6FBA-9026-4D69-828E-FD7068673E57","DF36F493-463F-4123-83F9-6B135DEEB7BA"]' (Size = 235) +@tags1='34c8d86e-a4ac-4be5-827f-584dda348a07' +@tags2='70534e05-782c-4052-8720-c2c54481ce5f' +@tags3='a7be028a-0cf2-448f-ab55-ce8bc5d8cf69' +@tags4='a8ad98f9-e023-4e2a-9a70-c2728455bd34' +@tags5='b39a6fba-9026-4d69-828e-fd7068673e57' +@tags6='df36f493-463f-4123-83f9-6b135deeb7ba' SELECT "g"."Nickname", "g"."SquadId", "g"."AssignedCityName", "g"."CityOfBirthName", "g"."Discriminator", "g"."FullName", "g"."HasSoulPatch", "g"."LeaderNickname", "g"."LeaderSquadId", "g"."Rank", "t"."Id", "t"."GearNickName", "t"."GearSquadId", "t"."IssueDate", "t"."Note" FROM "Gears" AS "g" LEFT JOIN "Tags" AS "t" ON "g"."Nickname" = "t"."GearNickName" AND "g"."SquadId" = "t"."GearSquadId" -WHERE "t"."Id" IS NOT NULL AND "t"."Id" IN ( - SELECT "t0"."value" - FROM json_each(@tags) AS "t0" -) +WHERE "t"."Id" IS NOT NULL AND "t"."Id" IN (@tags1, @tags2, @tags3, @tags4, @tags5, @tags6) """); } @@ -4847,14 +4836,8 @@ public override async Task OrderBy_Contains_empty_list(bool async) AssertSql( """ -@ids='[]' (Size = 2) - SELECT "g"."Nickname", "g"."SquadId", "g"."AssignedCityName", "g"."CityOfBirthName", "g"."Discriminator", "g"."FullName", "g"."HasSoulPatch", "g"."LeaderNickname", "g"."LeaderSquadId", "g"."Rank" FROM "Gears" AS "g" -ORDER BY "g"."SquadId" IN ( - SELECT "i"."value" - FROM json_each(@ids) AS "i" -) """); } @@ -7134,14 +7117,13 @@ public override async Task Contains_with_local_nullable_guid_list_closure(bool a AssertSql( """ -@ids='["DF36F493-463F-4123-83F9-6B135DEEB7BA","23CBCF9B-CE14-45CF-AAFA-2C2667EBFDD3","AB1B82D7-88DB-42BD-A132-7EEF9AA68AF4"]' (Size = 118) +@ids1='df36f493-463f-4123-83f9-6b135deeb7ba' +@ids2='23cbcf9b-ce14-45cf-aafa-2c2667ebfdd3' +@ids3='ab1b82d7-88db-42bd-a132-7eef9aa68af4' SELECT "t"."Id", "t"."GearNickName", "t"."GearSquadId", "t"."IssueDate", "t"."Note" FROM "Tags" AS "t" -WHERE "t"."Id" IN ( - SELECT "i"."value" - FROM json_each(@ids) AS "i" -) +WHERE "t"."Id" IN (@ids1, @ids2, @ids3) """); } @@ -8703,7 +8685,8 @@ public override async Task Nav_expansion_inside_Contains_argument(bool async) AssertSql( """ -@numbers='[1,-1]' (Size = 6) +@numbers1='1' +@numbers2='-1' SELECT "g"."Nickname", "g"."SquadId", "g"."AssignedCityName", "g"."CityOfBirthName", "g"."Discriminator", "g"."FullName", "g"."HasSoulPatch", "g"."LeaderNickname", "g"."LeaderSquadId", "g"."Rank" FROM "Gears" AS "g" @@ -8713,10 +8696,7 @@ SELECT 1 FROM "Weapons" AS "w" WHERE "g"."FullName" = "w"."OwnerFullName") THEN 1 ELSE 0 -END IN ( - SELECT "n"."value" - FROM json_each(@numbers) AS "n" -) +END IN (@numbers1, @numbers2) """); } @@ -8726,7 +8706,7 @@ public override async Task Nav_expansion_with_member_pushdown_inside_Contains_ar AssertSql( """ -@weapons='["Marcus\u0027 Lancer","Dom\u0027s Gnasher"]' (Size = 44) +@weapons1='Marcus' Lancer' (Size = 14), @weapons2='Dom's Gnasher' (Size = 13) SELECT "g"."Nickname", "g"."SquadId", "g"."AssignedCityName", "g"."CityOfBirthName", "g"."Discriminator", "g"."FullName", "g"."HasSoulPatch", "g"."LeaderNickname", "g"."LeaderSquadId", "g"."Rank" FROM "Gears" AS "g" @@ -8735,10 +8715,7 @@ public override async Task Nav_expansion_with_member_pushdown_inside_Contains_ar FROM "Weapons" AS "w0" WHERE "g"."FullName" = "w0"."OwnerFullName" ORDER BY "w0"."Id" - LIMIT 1) IN ( - SELECT "w"."value" - FROM json_each(@weapons) AS "w" -) + LIMIT 1) IN (@weapons1, @weapons2) """); } @@ -8748,7 +8725,9 @@ public override async Task Subquery_inside_Take_argument(bool async) AssertSql( """ -@numbers='[0,1,2]' (Size = 7) +@numbers1='0' +@numbers2='1' +@numbers3='2' SELECT "g"."Nickname", "g"."SquadId", "w1"."Id", "w1"."AmmunitionType", "w1"."IsAutomatic", "w1"."Name", "w1"."OwnerFullName", "w1"."SynergyWithId" FROM "Gears" AS "g" @@ -8759,9 +8738,9 @@ LEFT JOIN ( FROM "Weapons" AS "w" ) AS "w0" WHERE "w0"."row" <= COALESCE(( - SELECT "n"."value" - FROM json_each(@numbers) AS "n" - ORDER BY "n"."value" + SELECT "n"."Value" + FROM (SELECT 1 AS "_ord", @numbers1 AS "Value" UNION ALL VALUES (2, @numbers2), (3, @numbers3)) AS "n" + ORDER BY "n"."Value" LIMIT 1 OFFSET 1), 0) ) AS "w1" ON "g"."FullName" = "w1"."OwnerFullName" ORDER BY "g"."Nickname", "g"."SquadId", "w1"."OwnerFullName", "w1"."Id" @@ -8912,41 +8891,31 @@ public override async Task Nested_contains_with_enum(bool async) AssertSql( """ -@ranks='[1]' (Size = 3) +@ranks1='1' @key='5f221fb9-66f4-442a-92c9-d97ed5989cc7' -@keys='["0A47BCB7-A1CB-4345-8944-C58F82D6AAC7","5F221FB9-66F4-442A-92C9-D97ED5989CC7"]' (Size = 79) +@keys1='0a47bcb7-a1cb-4345-8944-c58f82d6aac7' +@keys2='5f221fb9-66f4-442a-92c9-d97ed5989cc7' SELECT "g"."Nickname", "g"."SquadId", "g"."AssignedCityName", "g"."CityOfBirthName", "g"."Discriminator", "g"."FullName", "g"."HasSoulPatch", "g"."LeaderNickname", "g"."LeaderSquadId", "g"."Rank" FROM "Gears" AS "g" WHERE CASE - WHEN "g"."Rank" IN ( - SELECT "r"."value" - FROM json_each(@ranks) AS "r" - ) THEN @key + WHEN "g"."Rank" = @ranks1 THEN @key ELSE @key -END IN ( - SELECT "k"."value" - FROM json_each(@keys) AS "k" -) +END IN (@keys1, @keys2) """, // """ -@ammoTypes='[1]' (Size = 3) +@ammoTypes1='1' @key='5f221fb9-66f4-442a-92c9-d97ed5989cc7' -@keys='["0A47BCB7-A1CB-4345-8944-C58F82D6AAC7","5F221FB9-66F4-442A-92C9-D97ED5989CC7"]' (Size = 79) +@keys1='0a47bcb7-a1cb-4345-8944-c58f82d6aac7' +@keys2='5f221fb9-66f4-442a-92c9-d97ed5989cc7' SELECT "w"."Id", "w"."AmmunitionType", "w"."IsAutomatic", "w"."Name", "w"."OwnerFullName", "w"."SynergyWithId" FROM "Weapons" AS "w" WHERE CASE - WHEN "w"."AmmunitionType" IN ( - SELECT "a"."value" - FROM json_each(@ammoTypes) AS "a" - ) THEN @key + WHEN "w"."AmmunitionType" = @ammoTypes1 THEN @key ELSE @key -END IN ( - SELECT "k"."value" - FROM json_each(@keys) AS "k" -) +END IN (@keys1, @keys2) """); } diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/NonSharedPrimitiveCollectionsQuerySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/NonSharedPrimitiveCollectionsQuerySqliteTest.cs index ebfc82e21ec..c9a37a92328 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/NonSharedPrimitiveCollectionsQuerySqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/NonSharedPrimitiveCollectionsQuerySqliteTest.cs @@ -21,6 +21,13 @@ protected override DbContextOptionsBuilder SetTranslateParameterizedCollectionsT return optionsBuilder; } + protected override DbContextOptionsBuilder SetTranslateParameterizedCollectionsToExpandedParameters(DbContextOptionsBuilder optionsBuilder) + { + new SqliteDbContextOptionsBuilder(optionsBuilder).TranslateParameterizedCollectionsToExpandedParameters(); + + return optionsBuilder; + } + #region Support for specific element types public override async Task Array_of_int() @@ -351,9 +358,9 @@ SELECT COUNT(*) """); } - public override async Task Parameter_collection_of_ints_Contains_int_with_default_constants() + public override async Task Parameter_collection_Contains_with_default_constants() { - await base.Parameter_collection_of_ints_Contains_int_with_default_constants(); + await base.Parameter_collection_Contains_with_default_constants(); AssertSql( """ @@ -380,9 +387,9 @@ FROM json_each(@ids) AS "i" """); } - public override async Task Parameter_collection_of_ints_Contains_int_with_default_constants_EF_Parameter() + public override async Task Parameter_collection_Contains_with_default_constants_EF_Parameter() { - await base.Parameter_collection_of_ints_Contains_int_with_default_constants_EF_Parameter(); + await base.Parameter_collection_Contains_with_default_constants_EF_Parameter(); AssertSql( """ @@ -414,9 +421,9 @@ FROM json_each(@ids) AS "i" """); } - public override async Task Parameter_collection_of_ints_Contains_int_with_default_parameters() + public override async Task Parameter_collection_Contains_with_default_parameters() { - await base.Parameter_collection_of_ints_Contains_int_with_default_parameters(); + await base.Parameter_collection_Contains_with_default_parameters(); AssertSql( """ @@ -446,9 +453,9 @@ SELECT COUNT(*) """); } - public override async Task Parameter_collection_of_ints_Contains_int_with_default_parameters_EF_Constant() + public override async Task Parameter_collection_Contains_with_default_parameters_EF_Constant() { - await base.Parameter_collection_of_ints_Contains_int_with_default_parameters_EF_Constant(); + await base.Parameter_collection_Contains_with_default_parameters_EF_Constant(); AssertSql( """ @@ -458,6 +465,39 @@ public override async Task Parameter_collection_of_ints_Contains_int_with_defaul """); } + public override async Task Parameter_collection_Count_with_column_predicate_with_default_expanded_parameters() + { + await base.Parameter_collection_Count_with_column_predicate_with_default_expanded_parameters(); + + AssertSql( + """ +@ids1='2' +@ids2='999' + +SELECT "t"."Id" +FROM "TestEntity" AS "t" +WHERE ( + SELECT COUNT(*) + FROM (SELECT @ids1 AS "Value" UNION ALL VALUES (@ids2)) AS "i" + WHERE "i"."Value" > "t"."Id") = 1 +"""); + } + + public override async Task Parameter_collection_Contains_with_default_expanded_parameters() + { + await base.Parameter_collection_Contains_with_default_expanded_parameters(); + + AssertSql( + """ +@ints1='2' +@ints2='999' + +SELECT "t"."Id" +FROM "TestEntity" AS "t" +WHERE "t"."Id" IN (@ints1, @ints2) +"""); + } + protected override ITestStoreFactory TestStoreFactory => SqliteTestStoreFactory.Instance; } diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/NorthwindAggregateOperatorsQuerySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/NorthwindAggregateOperatorsQuerySqliteTest.cs index e6b3438c32c..674b7cde7ef 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/NorthwindAggregateOperatorsQuerySqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/NorthwindAggregateOperatorsQuerySqliteTest.cs @@ -129,13 +129,11 @@ public override async Task Contains_inside_aggregate_function_with_GroupBy(bool AssertSql( """ -@cities='["London","Berlin"]' (Size = 19) +@cities1='London' (Size = 6) +@cities2='Berlin' (Size = 6) SELECT COUNT(CASE - WHEN "c"."City" IN ( - SELECT "c0"."value" - FROM json_each(@cities) AS "c0" - ) THEN 1 + WHEN "c"."City" IN (@cities1, @cities2) THEN 1 END) FROM "Customers" AS "c" GROUP BY "c"."Country" @@ -148,13 +146,11 @@ public override async Task Contains_inside_Average_without_GroupBy(bool async) AssertSql( """ -@cities='["London","Berlin"]' (Size = 19) +@cities1='London' (Size = 6) +@cities2='Berlin' (Size = 6) SELECT AVG(CASE - WHEN "c"."City" IN ( - SELECT "c0"."value" - FROM json_each(@cities) AS "c0" - ) THEN 1.0 + WHEN "c"."City" IN (@cities1, @cities2) THEN 1.0 ELSE 0.0 END) FROM "Customers" AS "c" @@ -167,13 +163,11 @@ public override async Task Contains_inside_Sum_without_GroupBy(bool async) AssertSql( """ -@cities='["London","Berlin"]' (Size = 19) +@cities1='London' (Size = 6) +@cities2='Berlin' (Size = 6) SELECT COALESCE(SUM(CASE - WHEN "c"."City" IN ( - SELECT "c0"."value" - FROM json_each(@cities) AS "c0" - ) THEN 1 + WHEN "c"."City" IN (@cities1, @cities2) THEN 1 ELSE 0 END), 0) FROM "Customers" AS "c" @@ -186,14 +180,12 @@ public override async Task Contains_inside_Count_without_GroupBy(bool async) AssertSql( """ -@cities='["London","Berlin"]' (Size = 19) +@cities1='London' (Size = 6) +@cities2='Berlin' (Size = 6) SELECT COUNT(*) FROM "Customers" AS "c" -WHERE "c"."City" IN ( - SELECT "c0"."value" - FROM json_each(@cities) AS "c0" -) +WHERE "c"."City" IN (@cities1, @cities2) """); } @@ -203,14 +195,12 @@ public override async Task Contains_inside_LongCount_without_GroupBy(bool async) AssertSql( """ -@cities='["London","Berlin"]' (Size = 19) +@cities1='London' (Size = 6) +@cities2='Berlin' (Size = 6) SELECT COUNT(*) FROM "Customers" AS "c" -WHERE "c"."City" IN ( - SELECT "c0"."value" - FROM json_each(@cities) AS "c0" -) +WHERE "c"."City" IN (@cities1, @cities2) """); } @@ -220,13 +210,11 @@ public override async Task Contains_inside_Max_without_GroupBy(bool async) AssertSql( """ -@cities='["London","Berlin"]' (Size = 19) +@cities1='London' (Size = 6) +@cities2='Berlin' (Size = 6) SELECT MAX(CASE - WHEN "c"."City" IN ( - SELECT "c0"."value" - FROM json_each(@cities) AS "c0" - ) THEN 1 + WHEN "c"."City" IN (@cities1, @cities2) THEN 1 ELSE 0 END) FROM "Customers" AS "c" @@ -239,13 +227,11 @@ public override async Task Contains_inside_Min_without_GroupBy(bool async) AssertSql( """ -@cities='["London","Berlin"]' (Size = 19) +@cities1='London' (Size = 6) +@cities2='Berlin' (Size = 6) SELECT MIN(CASE - WHEN "c"."City" IN ( - SELECT "c0"."value" - FROM json_each(@cities) AS "c0" - ) THEN 1 + WHEN "c"."City" IN (@cities1, @cities2) THEN 1 ELSE 0 END) FROM "Customers" AS "c" diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/PrimitiveCollectionsQuerySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/PrimitiveCollectionsQuerySqliteTest.cs index 4260649414b..d9c37297b54 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/PrimitiveCollectionsQuerySqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/PrimitiveCollectionsQuerySqliteTest.cs @@ -471,14 +471,15 @@ public override async Task Parameter_collection_Count(bool async) AssertSql( """ -@ids='[2,999]' (Size = 7) +@ids1='2' +@ids2='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"."NullableString", "p"."NullableStrings", "p"."NullableWrappedId", "p"."NullableWrappedIdWithNullableComparer", "p"."String", "p"."Strings", "p"."WrappedId" FROM "PrimitiveCollectionsEntity" AS "p" WHERE ( SELECT COUNT(*) - FROM json_each(@ids) AS "i" - WHERE "i"."value" > "p"."Id") = 1 + FROM (SELECT @ids1 AS "Value" UNION ALL VALUES (@ids2)) AS "i" + WHERE "i"."Value" > "p"."Id") = 1 """); } @@ -488,25 +489,21 @@ public override async Task Parameter_collection_of_ints_Contains_int(bool async) AssertSql( """ -@ints='[10,999]' (Size = 8) +@ints1='10' +@ints2='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"."NullableString", "p"."NullableStrings", "p"."NullableWrappedId", "p"."NullableWrappedIdWithNullableComparer", "p"."String", "p"."Strings", "p"."WrappedId" FROM "PrimitiveCollectionsEntity" AS "p" -WHERE "p"."Int" IN ( - SELECT "i"."value" - FROM json_each(@ints) AS "i" -) +WHERE "p"."Int" IN (@ints1, @ints2) """, // """ -@ints='[10,999]' (Size = 8) +@ints1='10' +@ints2='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"."NullableString", "p"."NullableStrings", "p"."NullableWrappedId", "p"."NullableWrappedIdWithNullableComparer", "p"."String", "p"."Strings", "p"."WrappedId" FROM "PrimitiveCollectionsEntity" AS "p" -WHERE "p"."Int" NOT IN ( - SELECT "i"."value" - FROM json_each(@ints) AS "i" -) +WHERE "p"."Int" NOT IN (@ints1, @ints2) """); } @@ -516,25 +513,21 @@ public override async Task Parameter_collection_HashSet_of_ints_Contains_int(boo AssertSql( """ -@ints='[10,999]' (Size = 8) +@ints1='10' +@ints2='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"."NullableString", "p"."NullableStrings", "p"."NullableWrappedId", "p"."NullableWrappedIdWithNullableComparer", "p"."String", "p"."Strings", "p"."WrappedId" FROM "PrimitiveCollectionsEntity" AS "p" -WHERE "p"."Int" IN ( - SELECT "i"."value" - FROM json_each(@ints) AS "i" -) +WHERE "p"."Int" IN (@ints1, @ints2) """, // """ -@ints='[10,999]' (Size = 8) +@ints1='10' +@ints2='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"."NullableString", "p"."NullableStrings", "p"."NullableWrappedId", "p"."NullableWrappedIdWithNullableComparer", "p"."String", "p"."Strings", "p"."WrappedId" FROM "PrimitiveCollectionsEntity" AS "p" -WHERE "p"."Int" NOT IN ( - SELECT "i"."value" - FROM json_each(@ints) AS "i" -) +WHERE "p"."Int" NOT IN (@ints1, @ints2) """); } @@ -544,25 +537,21 @@ public override async Task Parameter_collection_ImmutableArray_of_ints_Contains_ AssertSql( """ -@ints='[10,999]' (Nullable = false) (Size = 8) +@ints1='10' +@ints2='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"."NullableString", "p"."NullableStrings", "p"."NullableWrappedId", "p"."NullableWrappedIdWithNullableComparer", "p"."String", "p"."Strings", "p"."WrappedId" FROM "PrimitiveCollectionsEntity" AS "p" -WHERE "p"."Int" IN ( - SELECT "i"."value" - FROM json_each(@ints) AS "i" -) +WHERE "p"."Int" IN (@ints1, @ints2) """, // """ -@ints='[10,999]' (Nullable = false) (Size = 8) +@ints1='10' +@ints2='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"."NullableString", "p"."NullableStrings", "p"."NullableWrappedId", "p"."NullableWrappedIdWithNullableComparer", "p"."String", "p"."Strings", "p"."WrappedId" FROM "PrimitiveCollectionsEntity" AS "p" -WHERE "p"."Int" NOT IN ( - SELECT "i"."value" - FROM json_each(@ints) AS "i" -) +WHERE "p"."Int" NOT IN (@ints1, @ints2) """); } @@ -572,25 +561,21 @@ public override async Task Parameter_collection_of_ints_Contains_nullable_int(bo AssertSql( """ -@ints='[10,999]' (Size = 8) +@ints1='10' +@ints2='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"."NullableString", "p"."NullableStrings", "p"."NullableWrappedId", "p"."NullableWrappedIdWithNullableComparer", "p"."String", "p"."Strings", "p"."WrappedId" FROM "PrimitiveCollectionsEntity" AS "p" -WHERE "p"."NullableInt" IN ( - SELECT "i"."value" - FROM json_each(@ints) AS "i" -) +WHERE "p"."NullableInt" IN (@ints1, @ints2) """, // """ -@ints='[10,999]' (Size = 8) +@ints1='10' +@ints2='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"."NullableString", "p"."NullableStrings", "p"."NullableWrappedId", "p"."NullableWrappedIdWithNullableComparer", "p"."String", "p"."Strings", "p"."WrappedId" FROM "PrimitiveCollectionsEntity" AS "p" -WHERE "p"."NullableInt" NOT IN ( - SELECT "i"."value" - FROM json_each(@ints) AS "i" -) OR "p"."NullableInt" IS NULL +WHERE "p"."NullableInt" NOT IN (@ints1, @ints2) OR "p"."NullableInt" IS NULL """); } @@ -600,25 +585,21 @@ public override async Task Parameter_collection_of_nullable_ints_Contains_int(bo AssertSql( """ -@nullableInts='[10,999]' (Size = 8) +@nullableInts1='10' +@nullableInts2='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"."NullableString", "p"."NullableStrings", "p"."NullableWrappedId", "p"."NullableWrappedIdWithNullableComparer", "p"."String", "p"."Strings", "p"."WrappedId" FROM "PrimitiveCollectionsEntity" AS "p" -WHERE "p"."Int" IN ( - SELECT "n"."value" - FROM json_each(@nullableInts) AS "n" -) +WHERE "p"."Int" IN (@nullableInts1, @nullableInts2) """, // """ -@nullableInts='[10,999]' (Size = 8) +@nullableInts1='10' +@nullableInts2='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"."NullableString", "p"."NullableStrings", "p"."NullableWrappedId", "p"."NullableWrappedIdWithNullableComparer", "p"."String", "p"."Strings", "p"."WrappedId" FROM "PrimitiveCollectionsEntity" AS "p" -WHERE "p"."Int" NOT IN ( - SELECT "n"."value" - FROM json_each(@nullableInts) AS "n" -) +WHERE "p"."Int" NOT IN (@nullableInts1, @nullableInts2) """); } @@ -628,25 +609,19 @@ public override async Task Parameter_collection_of_nullable_ints_Contains_nullab AssertSql( """ -@nullableInts_without_nulls='[999]' (Size = 5) +@nullableInts2='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"."NullableString", "p"."NullableStrings", "p"."NullableWrappedId", "p"."NullableWrappedIdWithNullableComparer", "p"."String", "p"."Strings", "p"."WrappedId" FROM "PrimitiveCollectionsEntity" AS "p" -WHERE "p"."NullableInt" IN ( - SELECT "n"."value" - FROM json_each(@nullableInts_without_nulls) AS "n" -) OR "p"."NullableInt" IS NULL +WHERE "p"."NullableInt" IS NULL OR "p"."NullableInt" = @nullableInts2 """, // """ -@nullableInts_without_nulls='[999]' (Size = 5) +@nullableInts2='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"."NullableString", "p"."NullableStrings", "p"."NullableWrappedId", "p"."NullableWrappedIdWithNullableComparer", "p"."String", "p"."Strings", "p"."WrappedId" FROM "PrimitiveCollectionsEntity" AS "p" -WHERE "p"."NullableInt" NOT IN ( - SELECT "n"."value" - FROM json_each(@nullableInts_without_nulls) AS "n" -) AND "p"."NullableInt" IS NOT NULL +WHERE "p"."NullableInt" IS NOT NULL AND "p"."NullableInt" <> @nullableInts2 """); } @@ -656,25 +631,21 @@ public override async Task Parameter_collection_of_strings_Contains_string(bool AssertSql( """ -@strings='["10","999"]' (Size = 12) +@strings1='10' (Size = 2) +@strings2='999' (Size = 3) 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"."NullableWrappedId", "p"."NullableWrappedIdWithNullableComparer", "p"."String", "p"."Strings", "p"."WrappedId" FROM "PrimitiveCollectionsEntity" AS "p" -WHERE "p"."String" IN ( - SELECT "s"."value" - FROM json_each(@strings) AS "s" -) +WHERE "p"."String" IN (@strings1, @strings2) """, // """ -@strings='["10","999"]' (Size = 12) +@strings1='10' (Size = 2) +@strings2='999' (Size = 3) 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"."NullableWrappedId", "p"."NullableWrappedIdWithNullableComparer", "p"."String", "p"."Strings", "p"."WrappedId" FROM "PrimitiveCollectionsEntity" AS "p" -WHERE "p"."String" NOT IN ( - SELECT "s"."value" - FROM json_each(@strings) AS "s" -) +WHERE "p"."String" NOT IN (@strings1, @strings2) """); } @@ -684,25 +655,21 @@ public override async Task Parameter_collection_of_strings_Contains_nullable_str AssertSql( """ -@strings='["10","999"]' (Size = 12) +@strings1='10' (Size = 2) +@strings2='999' (Size = 3) 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"."NullableWrappedId", "p"."NullableWrappedIdWithNullableComparer", "p"."String", "p"."Strings", "p"."WrappedId" FROM "PrimitiveCollectionsEntity" AS "p" -WHERE "p"."NullableString" IN ( - SELECT "s"."value" - FROM json_each(@strings) AS "s" -) +WHERE "p"."NullableString" IN (@strings1, @strings2) """, // """ -@strings='["10","999"]' (Size = 12) +@strings1='10' (Size = 2) +@strings2='999' (Size = 3) 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"."NullableWrappedId", "p"."NullableWrappedIdWithNullableComparer", "p"."String", "p"."Strings", "p"."WrappedId" FROM "PrimitiveCollectionsEntity" AS "p" -WHERE "p"."NullableString" NOT IN ( - SELECT "s"."value" - FROM json_each(@strings) AS "s" -) OR "p"."NullableString" IS NULL +WHERE "p"."NullableString" NOT IN (@strings1, @strings2) OR "p"."NullableString" IS NULL """); } @@ -712,25 +679,19 @@ public override async Task Parameter_collection_of_nullable_strings_Contains_str AssertSql( """ -@strings='["10",null]' (Size = 11) +@strings1='10' (Size = 2) 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"."NullableWrappedId", "p"."NullableWrappedIdWithNullableComparer", "p"."String", "p"."Strings", "p"."WrappedId" FROM "PrimitiveCollectionsEntity" AS "p" -WHERE "p"."String" IN ( - SELECT "s"."value" - FROM json_each(@strings) AS "s" -) +WHERE "p"."String" = @strings1 """, // """ -@strings_without_nulls='["10"]' (Size = 6) +@strings1='10' (Size = 2) 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"."NullableWrappedId", "p"."NullableWrappedIdWithNullableComparer", "p"."String", "p"."Strings", "p"."WrappedId" FROM "PrimitiveCollectionsEntity" AS "p" -WHERE "p"."String" NOT IN ( - SELECT "s"."value" - FROM json_each(@strings_without_nulls) AS "s" -) +WHERE "p"."String" <> @strings1 """); } @@ -740,25 +701,19 @@ public override async Task Parameter_collection_of_nullable_strings_Contains_nul AssertSql( """ -@strings_without_nulls='["999"]' (Size = 7) +@strings1='999' (Size = 3) 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"."NullableWrappedId", "p"."NullableWrappedIdWithNullableComparer", "p"."String", "p"."Strings", "p"."WrappedId" FROM "PrimitiveCollectionsEntity" AS "p" -WHERE "p"."NullableString" IN ( - SELECT "s"."value" - FROM json_each(@strings_without_nulls) AS "s" -) OR "p"."NullableString" IS NULL +WHERE "p"."NullableString" IS NULL OR "p"."NullableString" = @strings1 """, // """ -@strings_without_nulls='["999"]' (Size = 7) +@strings1='999' (Size = 3) 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"."NullableWrappedId", "p"."NullableWrappedIdWithNullableComparer", "p"."String", "p"."Strings", "p"."WrappedId" FROM "PrimitiveCollectionsEntity" AS "p" -WHERE "p"."NullableString" NOT IN ( - SELECT "s"."value" - FROM json_each(@strings_without_nulls) AS "s" -) AND "p"."NullableString" IS NOT NULL +WHERE "p"."NullableString" IS NOT NULL AND "p"."NullableString" <> @strings1 """); } @@ -768,14 +723,12 @@ public override async Task Parameter_collection_of_DateTimes_Contains(bool async AssertSql( """ -@dateTimes='["2020-01-10 12:30:00","9999-01-01 00:00:00"]' (Size = 45) +@dateTimes1='2020-01-10T12:30:00.0000000Z' (DbType = DateTime) +@dateTimes2='9999-01-01T00:00:00.0000000Z' (DbType = DateTime) 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"."NullableWrappedId", "p"."NullableWrappedIdWithNullableComparer", "p"."String", "p"."Strings", "p"."WrappedId" FROM "PrimitiveCollectionsEntity" AS "p" -WHERE "p"."DateTime" IN ( - SELECT "d"."value" - FROM json_each(@dateTimes) AS "d" -) +WHERE "p"."DateTime" IN (@dateTimes1, @dateTimes2) """); } @@ -785,14 +738,11 @@ public override async Task Parameter_collection_of_bools_Contains(bool async) AssertSql( """ -@bools='[true]' (Size = 6) +@bools1='True' 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"."NullableWrappedId", "p"."NullableWrappedIdWithNullableComparer", "p"."String", "p"."Strings", "p"."WrappedId" FROM "PrimitiveCollectionsEntity" AS "p" -WHERE "p"."Bool" IN ( - SELECT "b"."value" - FROM json_each(@bools) AS "b" -) +WHERE "p"."Bool" = @bools1 """); } @@ -802,14 +752,12 @@ public override async Task Parameter_collection_of_enums_Contains(bool async) AssertSql( """ -@enums='[0,3]' (Size = 5) +@enums1='0' +@enums2='3' 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"."NullableWrappedId", "p"."NullableWrappedIdWithNullableComparer", "p"."String", "p"."Strings", "p"."WrappedId" FROM "PrimitiveCollectionsEntity" AS "p" -WHERE "p"."Enum" IN ( - SELECT "e"."value" - FROM json_each(@enums) AS "e" -) +WHERE "p"."Enum" IN (@enums1, @enums2) """); } @@ -821,10 +769,7 @@ public override async Task Parameter_collection_null_Contains(bool async) """ 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"."NullableWrappedId", "p"."NullableWrappedIdWithNullableComparer", "p"."String", "p"."Strings", "p"."WrappedId" FROM "PrimitiveCollectionsEntity" AS "p" -WHERE "p"."Int" IN ( - SELECT "i"."value" - FROM json_each(NULL) AS "i" -) +WHERE 0 """); } @@ -833,7 +778,7 @@ public override async Task Parameter_collection_Contains_with_EF_Constant(bool a await base.Parameter_collection_Contains_with_EF_Constant(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"."NullableWrappedId", "p"."NullableWrappedIdWithNullableComparer", "p"."String", "p"."Strings", "p"."WrappedId" FROM "PrimitiveCollectionsEntity" AS "p" WHERE "p"."Id" IN (2, 999, 1000) @@ -870,6 +815,14 @@ SELECT COUNT(*) """); } + // nothing to test here + public override Task Parameter_collection_Count_with_huge_number_of_values(bool async) + => base.Parameter_collection_Count_with_huge_number_of_values(async); + + // nothing to test here + public override Task Parameter_collection_of_ints_Contains_int_with_huge_number_of_values(bool async) + => base.Parameter_collection_of_ints_Contains_int_with_huge_number_of_values(async); + public override async Task Column_collection_of_ints_Contains(bool async) { await base.Column_collection_of_ints_Contains(async); @@ -1466,14 +1419,15 @@ public override async Task Column_collection_Join_parameter_collection(bool asyn AssertSql( """ -@ints='[11,111]' (Size = 8) +@ints1='11' +@ints2='111' 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"."NullableWrappedId", "p"."NullableWrappedIdWithNullableComparer", "p"."String", "p"."Strings", "p"."WrappedId" FROM "PrimitiveCollectionsEntity" AS "p" WHERE ( SELECT COUNT(*) FROM json_each("p"."Ints") AS "i" - INNER JOIN json_each(@ints) AS "i0" ON "i"."value" = "i0"."value") = 2 + INNER JOIN (SELECT @ints1 AS "Value" UNION ALL VALUES (@ints2)) AS "i0" ON "i"."value" = "i0"."Value") = 2 """); } @@ -1501,15 +1455,18 @@ await Assert.ThrowsAsync( AssertSql( """ -@ints='[11,111]' (Size = 8) +@ints1='11' +@ints2='111' 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"."NullableWrappedId", "p"."NullableWrappedIdWithNullableComparer", "p"."String", "p"."Strings", "p"."WrappedId" FROM "PrimitiveCollectionsEntity" AS "p" WHERE ( SELECT COUNT(*) FROM ( - SELECT 1 - FROM json_each(@ints) AS "i" + SELECT * FROM ( + SELECT 1 + FROM (SELECT @ints1 AS "Value" UNION ALL VALUES (@ints2)) AS "i" + ) UNION ALL SELECT 1 FROM json_each("p"."Ints") AS "i0" @@ -1539,7 +1496,8 @@ public override async Task Column_collection_Union_parameter_collection(bool asy AssertSql( """ -@ints='[11,111]' (Size = 8) +@ints1='11' +@ints2='111' 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"."NullableWrappedId", "p"."NullableWrappedIdWithNullableComparer", "p"."String", "p"."Strings", "p"."WrappedId" FROM "PrimitiveCollectionsEntity" AS "p" @@ -1549,8 +1507,9 @@ SELECT COUNT(*) SELECT "i"."value" FROM json_each("p"."Ints") AS "i" UNION - SELECT "i0"."value" - FROM json_each(@ints) AS "i0" + SELECT * FROM ( + SELECT @ints1 AS "Value" UNION ALL VALUES (@ints2) + ) ) AS "u") = 2 """); } @@ -1569,7 +1528,9 @@ SELECT COUNT(*) SELECT "i"."value" FROM json_each("p"."Ints") AS "i" INTERSECT - SELECT CAST(11 AS INTEGER) AS "Value" UNION ALL VALUES (111) + SELECT * FROM ( + SELECT CAST(11 AS INTEGER) AS "Value" UNION ALL VALUES (111) + ) ) AS "i0") = 2 """); } @@ -1585,7 +1546,9 @@ public override async Task Inline_collection_Except_column_collection(bool async WHERE ( SELECT COUNT(*) FROM ( - SELECT CAST(11 AS INTEGER) AS "Value" UNION ALL VALUES (111) + SELECT * FROM ( + SELECT CAST(11 AS INTEGER) AS "Value" UNION ALL VALUES (111) + ) EXCEPT SELECT "i"."value" AS "Value" FROM json_each("p"."Ints") AS "i" @@ -1667,19 +1630,20 @@ public override async Task Parameter_collection_in_subquery_Count_as_compiled_qu AssertSql( """ -@ints='[10,111]' (Size = 8) +@ints1='10' +@ints2='111' SELECT COUNT(*) FROM "PrimitiveCollectionsEntity" AS "p" WHERE ( SELECT COUNT(*) FROM ( - SELECT "i"."value" AS "value0" - FROM json_each(@ints) AS "i" - ORDER BY "i"."key" + SELECT "i"."Value" AS "Value0" + FROM (SELECT 1 AS "_ord", @ints1 AS "Value" UNION ALL VALUES (2, @ints2)) AS "i" + ORDER BY "i"."_ord" LIMIT -1 OFFSET 1 ) AS "i0" - WHERE "i0"."value0" > "p"."Id") = 1 + WHERE "i0"."Value0" > "p"."Id") = 1 """); } @@ -1696,22 +1660,23 @@ public override async Task Parameter_collection_in_subquery_Union_column_collect AssertSql( """ -@ints='[10,111]' (Size = 8) +@ints1='10' +@ints2='111' 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"."NullableWrappedId", "p"."NullableWrappedIdWithNullableComparer", "p"."String", "p"."Strings", "p"."WrappedId" FROM "PrimitiveCollectionsEntity" AS "p" WHERE ( SELECT COUNT(*) FROM ( - SELECT "i1"."value" + SELECT "i1"."Value" FROM ( - SELECT "i"."value" - FROM json_each(@ints) AS "i" - ORDER BY "i"."key" + SELECT "i"."Value" + FROM (SELECT 1 AS "_ord", @ints1 AS "Value" UNION ALL VALUES (2, @ints2)) AS "i" + ORDER BY "i"."_ord" LIMIT -1 OFFSET 1 ) AS "i1" UNION - SELECT "i0"."value" + SELECT "i0"."value" AS "Value" FROM json_each("p"."Ints") AS "i0" ) AS "u") = 3 """); @@ -1723,17 +1688,16 @@ public override async Task Parameter_collection_in_subquery_Union_column_collect AssertSql( """ -@Skip='[111]' (Size = 5) +@Skip1='111' 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"."NullableWrappedId", "p"."NullableWrappedIdWithNullableComparer", "p"."String", "p"."Strings", "p"."WrappedId" FROM "PrimitiveCollectionsEntity" AS "p" WHERE ( SELECT COUNT(*) FROM ( - SELECT "s"."value" - FROM json_each(@Skip) AS "s" + SELECT @Skip1 AS "Value" UNION - SELECT "i"."value" + SELECT "i"."value" AS "Value" FROM json_each("p"."Ints") AS "i" ) AS "u") = 3 """); @@ -1745,17 +1709,16 @@ public override async Task Parameter_collection_in_subquery_Union_column_collect AssertSql( """ -@Skip='[111]' (Size = 5) +@Skip1='111' 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"."NullableWrappedId", "p"."NullableWrappedIdWithNullableComparer", "p"."String", "p"."Strings", "p"."WrappedId" FROM "PrimitiveCollectionsEntity" AS "p" WHERE ( SELECT COUNT(*) FROM ( - SELECT "s"."value" - FROM json_each(@Skip) AS "s" + SELECT @Skip1 AS "Value" UNION - SELECT "i2"."value" + SELECT "i2"."value" AS "Value" FROM ( SELECT "i1"."value" FROM ( @@ -1787,7 +1750,8 @@ public override async Task Column_collection_in_subquery_Union_parameter_collect AssertSql( """ -@ints='[10,111]' (Size = 8) +@ints1='10' +@ints2='111' 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"."NullableWrappedId", "p"."NullableWrappedIdWithNullableComparer", "p"."String", "p"."Strings", "p"."WrappedId" FROM "PrimitiveCollectionsEntity" AS "p" @@ -1802,8 +1766,9 @@ ORDER BY "i"."key" LIMIT -1 OFFSET 1 ) AS "i1" UNION - SELECT "i0"."value" - FROM json_each(@ints) AS "i0" + SELECT * FROM ( + SELECT @ints1 AS "Value" UNION ALL VALUES (@ints2) + ) ) AS "u") = 3 """); } @@ -1923,21 +1888,19 @@ public override async Task Nested_contains_with_Lists_and_no_inferred_type_mappi AssertSql( """ -@ints='[1,2,3]' (Size = 7) -@strings='["one","two","three"]' (Size = 21) +@ints1='1' +@ints2='2' +@ints3='3' +@strings1='one' (Size = 3) +@strings2='two' (Size = 3) +@strings3='three' (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"."NullableString", "p"."NullableStrings", "p"."NullableWrappedId", "p"."NullableWrappedIdWithNullableComparer", "p"."String", "p"."Strings", "p"."WrappedId" FROM "PrimitiveCollectionsEntity" AS "p" WHERE CASE - WHEN "p"."Int" IN ( - SELECT "i"."value" - FROM json_each(@ints) AS "i" - ) THEN 'one' + WHEN "p"."Int" IN (@ints1, @ints2, @ints3) THEN 'one' ELSE 'two' -END IN ( - SELECT "s"."value" - FROM json_each(@strings) AS "s" -) +END IN (@strings1, @strings2, @strings3) """); } @@ -1947,21 +1910,19 @@ public override async Task Nested_contains_with_arrays_and_no_inferred_type_mapp AssertSql( """ -@ints='[1,2,3]' (Size = 7) -@strings='["one","two","three"]' (Size = 21) +@ints1='1' +@ints2='2' +@ints3='3' +@strings1='one' (Size = 3) +@strings2='two' (Size = 3) +@strings3='three' (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"."NullableString", "p"."NullableStrings", "p"."NullableWrappedId", "p"."NullableWrappedIdWithNullableComparer", "p"."String", "p"."Strings", "p"."WrappedId" FROM "PrimitiveCollectionsEntity" AS "p" WHERE CASE - WHEN "p"."Int" IN ( - SELECT "i"."value" - FROM json_each(@ints) AS "i" - ) THEN 'one' + WHEN "p"."Int" IN (@ints1, @ints2, @ints3) THEN 'one' ELSE 'two' -END IN ( - SELECT "s"."value" - FROM json_each(@strings) AS "s" -) +END IN (@strings1, @strings2, @strings3) """); } @@ -2014,25 +1975,21 @@ public override async Task Parameter_collection_of_structs_Contains_struct(bool AssertSql( """ -@values='[22,33]' (Size = 7) +@values1='22' +@values2='33' 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"."NullableWrappedId", "p"."NullableWrappedIdWithNullableComparer", "p"."String", "p"."Strings", "p"."WrappedId" FROM "PrimitiveCollectionsEntity" AS "p" -WHERE "p"."WrappedId" IN ( - SELECT "v"."value" - FROM json_each(@values) AS "v" -) +WHERE "p"."WrappedId" IN (@values1, @values2) """, // """ -@values='[11,44]' (Size = 7) +@values1='11' +@values2='44' 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"."NullableWrappedId", "p"."NullableWrappedIdWithNullableComparer", "p"."String", "p"."Strings", "p"."WrappedId" FROM "PrimitiveCollectionsEntity" AS "p" -WHERE "p"."WrappedId" NOT IN ( - SELECT "v"."value" - FROM json_each(@values) AS "v" -) +WHERE "p"."WrappedId" NOT IN (@values1, @values2) """); } @@ -2042,25 +1999,21 @@ public override async Task Parameter_collection_of_structs_Contains_nullable_str AssertSql( """ -@values='[22,33]' (Size = 7) +@values1='22' +@values2='33' 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"."NullableWrappedId", "p"."NullableWrappedIdWithNullableComparer", "p"."String", "p"."Strings", "p"."WrappedId" FROM "PrimitiveCollectionsEntity" AS "p" -WHERE "p"."NullableWrappedId" IN ( - SELECT "v"."value" - FROM json_each(@values) AS "v" -) +WHERE "p"."NullableWrappedId" IN (@values1, @values2) """, // """ -@values='[11,44]' (Size = 7) +@values1='11' +@values2='44' 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"."NullableWrappedId", "p"."NullableWrappedIdWithNullableComparer", "p"."String", "p"."Strings", "p"."WrappedId" FROM "PrimitiveCollectionsEntity" AS "p" -WHERE "p"."NullableWrappedId" NOT IN ( - SELECT "v"."value" - FROM json_each(@values) AS "v" -) OR "p"."NullableWrappedId" IS NULL +WHERE "p"."NullableWrappedId" NOT IN (@values1, @values2) OR "p"."NullableWrappedId" IS NULL """); } @@ -2070,25 +2023,21 @@ public override async Task Parameter_collection_of_structs_Contains_nullable_str AssertSql( """ -@values='[22,33]' (Size = 7) +@values1='22' +@values2='33' 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"."NullableWrappedId", "p"."NullableWrappedIdWithNullableComparer", "p"."String", "p"."Strings", "p"."WrappedId" FROM "PrimitiveCollectionsEntity" AS "p" -WHERE "p"."NullableWrappedIdWithNullableComparer" IN ( - SELECT "v"."value" - FROM json_each(@values) AS "v" -) +WHERE "p"."NullableWrappedIdWithNullableComparer" IN (@values1, @values2) """, // """ -@values='[11,44]' (Size = 7) +@values1='11' +@values2='44' 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"."NullableWrappedId", "p"."NullableWrappedIdWithNullableComparer", "p"."String", "p"."Strings", "p"."WrappedId" FROM "PrimitiveCollectionsEntity" AS "p" -WHERE "p"."NullableWrappedId" NOT IN ( - SELECT "v"."value" - FROM json_each(@values) AS "v" -) OR "p"."NullableWrappedId" IS NULL +WHERE "p"."NullableWrappedId" NOT IN (@values1, @values2) OR "p"."NullableWrappedId" IS NULL """); } @@ -2098,25 +2047,20 @@ public override async Task Parameter_collection_of_nullable_structs_Contains_str AssertSql( """ -@values='[null,22]' (Size = 9) +@values2='22' 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"."NullableWrappedId", "p"."NullableWrappedIdWithNullableComparer", "p"."String", "p"."Strings", "p"."WrappedId" FROM "PrimitiveCollectionsEntity" AS "p" -WHERE "p"."WrappedId" IN ( - SELECT "v"."value" - FROM json_each(@values) AS "v" -) +WHERE "p"."WrappedId" = @values2 """, // """ -@values='[11,44]' (Size = 7) +@values1='11' +@values2='44' 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"."NullableWrappedId", "p"."NullableWrappedIdWithNullableComparer", "p"."String", "p"."Strings", "p"."WrappedId" FROM "PrimitiveCollectionsEntity" AS "p" -WHERE "p"."WrappedId" NOT IN ( - SELECT "v"."value" - FROM json_each(@values) AS "v" -) +WHERE "p"."WrappedId" NOT IN (@values1, @values2) """); } @@ -2126,25 +2070,20 @@ public override async Task Parameter_collection_of_nullable_structs_Contains_nul AssertSql( """ -@values_without_nulls='[22]' (Size = 4) +@values2='22' 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"."NullableWrappedId", "p"."NullableWrappedIdWithNullableComparer", "p"."String", "p"."Strings", "p"."WrappedId" FROM "PrimitiveCollectionsEntity" AS "p" -WHERE "p"."NullableWrappedId" IN ( - SELECT "v"."value" - FROM json_each(@values_without_nulls) AS "v" -) OR "p"."NullableWrappedId" IS NULL +WHERE "p"."NullableWrappedId" IS NULL OR "p"."NullableWrappedId" = @values2 """, // """ -@values='[11,44]' (Size = 7) +@values1='11' +@values2='44' 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"."NullableWrappedId", "p"."NullableWrappedIdWithNullableComparer", "p"."String", "p"."Strings", "p"."WrappedId" FROM "PrimitiveCollectionsEntity" AS "p" -WHERE "p"."NullableWrappedId" NOT IN ( - SELECT "v"."value" - FROM json_each(@values) AS "v" -) OR "p"."NullableWrappedId" IS NULL +WHERE "p"."NullableWrappedId" NOT IN (@values1, @values2) OR "p"."NullableWrappedId" IS NULL """); } @@ -2154,25 +2093,20 @@ public override async Task Parameter_collection_of_nullable_structs_Contains_nul AssertSql( """ -@values_without_nulls='[22]' (Size = 4) +@values2='22' 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"."NullableWrappedId", "p"."NullableWrappedIdWithNullableComparer", "p"."String", "p"."Strings", "p"."WrappedId" FROM "PrimitiveCollectionsEntity" AS "p" -WHERE "p"."NullableWrappedIdWithNullableComparer" IN ( - SELECT "v"."value" - FROM json_each(@values_without_nulls) AS "v" -) OR "p"."NullableWrappedIdWithNullableComparer" IS NULL +WHERE "p"."NullableWrappedIdWithNullableComparer" IS NULL OR "p"."NullableWrappedIdWithNullableComparer" = @values2 """, // """ -@values='[11,44]' (Size = 7) +@values1='11' +@values2='44' 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"."NullableWrappedId", "p"."NullableWrappedIdWithNullableComparer", "p"."String", "p"."Strings", "p"."WrappedId" FROM "PrimitiveCollectionsEntity" AS "p" -WHERE "p"."NullableWrappedIdWithNullableComparer" NOT IN ( - SELECT "v"."value" - FROM json_each(@values) AS "v" -) OR "p"."NullableWrappedIdWithNullableComparer" IS NULL +WHERE "p"."NullableWrappedIdWithNullableComparer" NOT IN (@values1, @values2) OR "p"."NullableWrappedIdWithNullableComparer" IS NULL """); }