diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 50696ebdd..0786730d6 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -29,15 +29,15 @@ jobs:
fail-fast: false
matrix:
dbVersion:
- - 8.0.36-mysql
- - 11.2.2-mariadb
- - 11.1.3-mariadb
- - 11.0.4-mariadb
- - 10.11.6-mariadb
- - 10.10.7-mariadb
- - 10.6.16-mariadb
- - 10.5.23-mariadb
- - 10.4.32-mariadb
+ - 8.4.3-mysql
+ - 8.0.40-mysql
+ - 11.6.2-mariadb
+ - 11.5.2-mariadb
+ - 11.4.4-mariadb
+ - 11.3.2-mariadb
+ - 10.11.10-mariadb
+ - 10.6.20-mariadb
+ - 10.5.27-mariadb
os:
- ubuntu-latest
- windows-latest
diff --git a/Dependencies.targets b/Dependencies.targets
index 3096890f9..c4a2bba44 100644
--- a/Dependencies.targets
+++ b/Dependencies.targets
@@ -1,6 +1,6 @@
- [9.0.0-preview.1.24081.2,9.0.0-preview.1.999999]
+ [9.0.0,9.0.999]
@@ -9,45 +9,49 @@
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
-
-
+
+
-
-
-
-
+
+
+
+
-
-
-
-
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Directory.Build.props b/Directory.Build.props
index f46a57707..13a3ed118 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -26,11 +26,11 @@
net8.0
- net8.0
+ net9.0
net8.0
- net8.0
+ net9.0
net8.0
- net7.0
+ net8.0
@@ -39,7 +39,7 @@
-
+
diff --git a/Version.props b/Version.props
index 039a3a1e9..71ed64c8f 100644
--- a/Version.props
+++ b/Version.props
@@ -13,8 +13,8 @@
- "servicing" - EF Core release independent, code quality production ready, mainly bugfixes
-->
9.0.0
- preview
- 2
+ alpha
+ 1
+ $(NoWarn);CS1591
@@ -48,7 +50,6 @@
-
diff --git a/src/EFCore.MySql/Infrastructure/MariaDbServerVersion.cs b/src/EFCore.MySql/Infrastructure/MariaDbServerVersion.cs
index 749463457..f3025d9da 100644
--- a/src/EFCore.MySql/Infrastructure/MariaDbServerVersion.cs
+++ b/src/EFCore.MySql/Infrastructure/MariaDbServerVersion.cs
@@ -19,6 +19,11 @@ public class MariaDbServerVersion : ServerVersion
public override ServerVersionSupport Supports { get; }
+ // MariaDB on Linux seems to be using uca1400 collations as the default for the utf8mb4 charset from 11.4.2 onwards, but MariaDB for
+ // Windows seems to be using it only in later minor versions.
+ public override string DefaultUtf8CsCollation => Version >= new Version(11, 4, 2) ? "utf8mb4_uca1400_as_cs" : "utf8mb4_bin";
+ public override string DefaultUtf8CiCollation => Version >= new Version(11, 4, 2) ? "utf8mb4_uca1400_ai_ci" : "utf8mb4_general_ci";
+
public MariaDbServerVersion(Version version)
: base(version, ServerType.MariaDb)
{
@@ -64,7 +69,7 @@ internal MariaDbServerVersionSupport([NotNull] ServerVersion serverVersion)
public override bool GeneratedColumns => ServerVersion.Version >= new Version(10, 2, 0);
public override bool NullableGeneratedColumns => false;
public override bool ParenthesisEnclosedGeneratedColumnExpressions => false;
- public override bool DefaultCharSetUtf8Mb4 => false;
+ public override bool DefaultCharSetUtf8Mb4 => ServerVersion.Version >= new Version(11, 4, 2);
public override bool DefaultExpression => false;
public override bool AlternativeDefaultExpression => ServerVersion.Version >= new Version(10, 2, 7);
public override bool SpatialIndexes => ServerVersion.Version >= new Version(10, 2, 2);
@@ -91,6 +96,8 @@ internal MariaDbServerVersionSupport([NotNull] ServerVersion serverVersion)
public override bool Values => ServerVersion.Version >= new Version(10, 3, 3);
public override bool ValuesWithRows => false;
public override bool WhereSubqueryReferencesOuterQuery => false;
+ public override bool FieldReferenceInTableValueConstructor => false;
+ public override bool CollationCharacterSetApplicabilityWithFullCollationNameColumn => ServerVersion.Version >= new Version(10, 10, 1);
public override bool JsonTableImplementationStable => false;
public override bool JsonTableImplementationWithoutMariaDbBugs => false;
diff --git a/src/EFCore.MySql/Infrastructure/MySqlServerVersion.cs b/src/EFCore.MySql/Infrastructure/MySqlServerVersion.cs
index b64d70fef..c0c548e7c 100644
--- a/src/EFCore.MySql/Infrastructure/MySqlServerVersion.cs
+++ b/src/EFCore.MySql/Infrastructure/MySqlServerVersion.cs
@@ -19,6 +19,9 @@ public class MySqlServerVersion : ServerVersion
public override ServerVersionSupport Supports { get; }
+ public override string DefaultUtf8CsCollation => Supports.DefaultCharSetUtf8Mb4 ? "utf8mb4_0900_as_cs" : "utf8mb4_bin";
+ public override string DefaultUtf8CiCollation => Supports.DefaultCharSetUtf8Mb4 ? "utf8mb4_0900_ai_ci" : "utf8mb4_general_ci";
+
public MySqlServerVersion(Version version)
: base(version, ServerType.MySql)
{
@@ -94,6 +97,8 @@ internal MySqlServerVersionSupport([NotNull] ServerVersion serverVersion)
public override bool Values => false;
public override bool ValuesWithRows => ServerVersion.Version >= new Version(8, 0, 19);
public override bool WhereSubqueryReferencesOuterQuery => false;
+ public override bool FieldReferenceInTableValueConstructor => true;
+ public override bool CollationCharacterSetApplicabilityWithFullCollationNameColumn => false;
public override bool JsonTableImplementationStable => false;
public override bool JsonTableImplementationWithoutMySqlBugs => false; // Other non-fatal bugs regarding JSON_TABLE.
diff --git a/src/EFCore.MySql/Infrastructure/ServerVersion.cs b/src/EFCore.MySql/Infrastructure/ServerVersion.cs
index 2ed6a65c5..42c991138 100644
--- a/src/EFCore.MySql/Infrastructure/ServerVersion.cs
+++ b/src/EFCore.MySql/Infrastructure/ServerVersion.cs
@@ -38,8 +38,8 @@ protected ServerVersion(Version version, ServerType type, string typeIdentifier
public virtual int MaxKeyLength => Supports.LargerKeyLength ? 3072 : 767;
public virtual CharSet DefaultCharSet => Supports.DefaultCharSetUtf8Mb4 ? CharSet.Utf8Mb4 : CharSet.Latin1;
- public virtual string DefaultUtf8CsCollation => Supports.DefaultCharSetUtf8Mb4 ? "utf8mb4_0900_as_cs" : "utf8mb4_bin";
- public virtual string DefaultUtf8CiCollation => Supports.DefaultCharSetUtf8Mb4 ? "utf8mb4_0900_ai_ci" : "utf8mb4_general_ci";
+ public abstract string DefaultUtf8CsCollation { get; }
+ public abstract string DefaultUtf8CiCollation { get; }
public override bool Equals(object obj)
=> obj is ServerVersion version &&
diff --git a/src/EFCore.MySql/Infrastructure/ServerVersionSupport.cs b/src/EFCore.MySql/Infrastructure/ServerVersionSupport.cs
index 644309070..a6dfe2c8b 100644
--- a/src/EFCore.MySql/Infrastructure/ServerVersionSupport.cs
+++ b/src/EFCore.MySql/Infrastructure/ServerVersionSupport.cs
@@ -94,6 +94,8 @@ public virtual bool PropertyOrVersion(string propertyNameOrServerVersion)
public virtual bool Values => false;
public virtual bool ValuesWithRows => false;
public virtual bool WhereSubqueryReferencesOuterQuery => false;
+ public virtual bool FieldReferenceInTableValueConstructor => false;
+ public virtual bool CollationCharacterSetApplicabilityWithFullCollationNameColumn => false;
public virtual bool JsonTableImplementationStable => JsonTable;
public virtual bool JsonTableImplementationWithoutMySqlBugs => JsonTable;
diff --git a/src/EFCore.MySql/Migrations/Internal/MySqlHistoryRepository.cs b/src/EFCore.MySql/Migrations/Internal/MySqlHistoryRepository.cs
index e77348d72..8d03a1dd7 100644
--- a/src/EFCore.MySql/Migrations/Internal/MySqlHistoryRepository.cs
+++ b/src/EFCore.MySql/Migrations/Internal/MySqlHistoryRepository.cs
@@ -4,8 +4,11 @@
using System;
using System.Linq;
using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Microsoft.EntityFrameworkCore.Metadata.Conventions;
@@ -28,6 +31,66 @@ public MySqlHistoryRepository([NotNull] HistoryRepositoryDependencies dependenci
_sqlGenerationHelper = (MySqlSqlGenerationHelper)dependencies.SqlGenerationHelper;
}
+ public override LockReleaseBehavior LockReleaseBehavior
+ => LockReleaseBehavior.Connection;
+
+ public override IMigrationsDatabaseLock AcquireDatabaseLock()
+ {
+ Dependencies.MigrationsLogger.AcquiringMigrationLock();
+
+ Dependencies.RawSqlCommandBuilder
+ .Build(GetAcquireLockCommandSql())
+ .ExecuteNonQuery(CreateRelationalCommandParameters());
+
+ return CreateMigrationDatabaseLock();
+ }
+
+ public override async Task AcquireDatabaseLockAsync(CancellationToken cancellationToken = default)
+ {
+ await Dependencies.RawSqlCommandBuilder
+ .Build(GetAcquireLockCommandSql())
+ .ExecuteNonQueryAsync(CreateRelationalCommandParameters(), cancellationToken)
+ .ConfigureAwait(false);
+
+ return CreateMigrationDatabaseLock();
+ }
+
+ ///
+ /// Returns the name of the database-wide for migrations. Currently, this is actully a database *server*-wide lock, so the lock
+ /// should contain the database name to make it more database specific.
+ ///
+ protected virtual string GetDatabaseLockName(string databaseName)
+ => $"__{databaseName}_EFMigrationsLock";
+
+ // We cannot use LOCK TABLES/UNLOCK TABLES, because we would need to know *all* the table we want to access by name beforehand,
+ // since after the LOCK TABLES statement has run, only the tables specified can be access and access to any other table results in
+ // an error.
+ // We use GET_LOCK()/RELEASE_LOCK() for now. We would like to not specify a timeout, because we cannot know how long the migration
+ // operations are supposed to take. However, while MySQL interprets negative timeout values as infinite, MariaDB does not. We
+ // therefore specify a very large timeout in seconds instead (currently 72 hours). If RELEASE_LOCK() is never called, the lock is automatically released
+ // when the session ends or is killed. This function pair is not bound to a database, but is a database server wide global mutex. We
+ // therefore explicitly use the database name as part of the lock name.
+ // If it turns out, that users want a replication-save method later, we could implement a locking table mechanism as Sqlite does.
+ private string GetAcquireLockCommandSql()
+ => $"SELECT GET_LOCK('{GetDatabaseLockName(Dependencies.Connection.DbConnection.Database)}', {60 * 60 * 24 * 3})";
+
+ private RelationalCommandParameterObject CreateRelationalCommandParameters()
+ => new(
+ Dependencies.Connection,
+ null,
+ null,
+ Dependencies.CurrentContext.Context,
+ Dependencies.CommandLogger, CommandSource.Migrations);
+
+ private MySqlMigrationDatabaseLock CreateMigrationDatabaseLock()
+ => new(
+ this,
+ CreateReleaseLockCommand(),
+ CreateRelationalCommandParameters());
+
+ private IRelationalCommand CreateReleaseLockCommand()
+ => Dependencies.RawSqlCommandBuilder.Build($"SELECT RELEASE_LOCK('{GetDatabaseLockName(Dependencies.Connection.DbConnection.Database)}')");
+
protected override void ConfigureTable([NotNull] EntityTypeBuilder history)
{
base.ConfigureTable(history);
@@ -175,5 +238,21 @@ protected override string ProductVersionColumnName
.GetColumnName();
#endregion Necessary implementation because we cannot directly override EnsureModel
+
+ private sealed class MySqlMigrationDatabaseLock(
+ MySqlHistoryRepository historyRepository,
+ IRelationalCommand releaseLockCommand,
+ RelationalCommandParameterObject relationalCommandParameters,
+ CancellationToken cancellationToken = default)
+ : IMigrationsDatabaseLock
+ {
+ public IHistoryRepository HistoryRepository => historyRepository;
+
+ public void Dispose()
+ => releaseLockCommand.ExecuteScalar(relationalCommandParameters);
+
+ public async ValueTask DisposeAsync()
+ => await releaseLockCommand.ExecuteScalarAsync(relationalCommandParameters, cancellationToken).ConfigureAwait(false);
+ }
}
}
diff --git a/src/EFCore.MySql/Migrations/Internal/MySqlMigrator.cs b/src/EFCore.MySql/Migrations/Internal/MySqlMigrator.cs
index 020cb96bc..a663c49f0 100644
--- a/src/EFCore.MySql/Migrations/Internal/MySqlMigrator.cs
+++ b/src/EFCore.MySql/Migrations/Internal/MySqlMigrator.cs
@@ -6,10 +6,10 @@
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
-using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations.Internal;
using Microsoft.EntityFrameworkCore.Migrations.Operations;
@@ -38,19 +38,23 @@ public class MySqlMigrator : Migrator
private readonly IRelationalCommandDiagnosticsLogger _commandLogger;
public MySqlMigrator(
- [NotNull] IMigrationsAssembly migrationsAssembly,
- [NotNull] IHistoryRepository historyRepository,
- [NotNull] IDatabaseCreator databaseCreator,
- [NotNull] IMigrationsSqlGenerator migrationsSqlGenerator,
- [NotNull] IRawSqlCommandBuilder rawSqlCommandBuilder,
- [NotNull] IMigrationCommandExecutor migrationCommandExecutor,
- [NotNull] IRelationalConnection connection,
- [NotNull] ISqlGenerationHelper sqlGenerationHelper,
- [NotNull] ICurrentDbContext currentContext,
- [NotNull] IModelRuntimeInitializer modelRuntimeInitializer,
- [NotNull] IDiagnosticsLogger logger,
- [NotNull] IRelationalCommandDiagnosticsLogger commandLogger,
- [NotNull] IDatabaseProvider databaseProvider)
+ IMigrationsAssembly migrationsAssembly,
+ IHistoryRepository historyRepository,
+ IDatabaseCreator databaseCreator,
+ IMigrationsSqlGenerator migrationsSqlGenerator,
+ IRawSqlCommandBuilder rawSqlCommandBuilder,
+ IMigrationCommandExecutor migrationCommandExecutor,
+ IRelationalConnection connection,
+ ISqlGenerationHelper sqlGenerationHelper,
+ ICurrentDbContext currentContext,
+ IModelRuntimeInitializer modelRuntimeInitializer,
+ IDiagnosticsLogger logger,
+ IRelationalCommandDiagnosticsLogger commandLogger,
+ IDatabaseProvider databaseProvider,
+ IMigrationsModelDiffer migrationsModelDiffer,
+ IDesignTimeModel designTimeModel,
+ IDbContextOptions contextOptions,
+ IExecutionStrategy executionStrategy)
: base(
migrationsAssembly,
historyRepository,
@@ -64,7 +68,11 @@ public MySqlMigrator(
modelRuntimeInitializer,
logger,
commandLogger,
- databaseProvider)
+ databaseProvider,
+ migrationsModelDiffer,
+ designTimeModel,
+ contextOptions,
+ executionStrategy)
{
_migrationsAssembly = migrationsAssembly;
_rawSqlCommandBuilder = rawSqlCommandBuilder;
@@ -145,13 +153,11 @@ protected virtual List GetAllMigrationOperations(string from
PopulateMigrations(
appliedMigrations,
toMigration,
- out var migrationsToApply,
- out var migrationsToRevert,
- out var actualTargetMigration);
+ out var migratorData);
- return migrationsToApply
+ return migratorData.AppliedMigrations
.SelectMany(x => x.UpOperations)
- .Concat(migrationsToRevert.SelectMany(x => x.DownOperations))
+ .Concat(migratorData.RevertedMigrations.SelectMany(x => x.DownOperations))
.ToList();
}
diff --git a/src/EFCore.MySql/Migrations/MySqlMigrationsSqlGenerator.cs b/src/EFCore.MySql/Migrations/MySqlMigrationsSqlGenerator.cs
index 390a17589..2ad1570a8 100644
--- a/src/EFCore.MySql/Migrations/MySqlMigrationsSqlGenerator.cs
+++ b/src/EFCore.MySql/Migrations/MySqlMigrationsSqlGenerator.cs
@@ -222,6 +222,31 @@ protected override void Generate(
[NotNull] MigrationCommandListBuilder builder,
bool terminate = true)
{
+ // Create a unique constraint for an AUTO_INCREMENT column that is part of a compound primary key, but is not the first column
+ // in that key. In this case, for InnoDB to be satisfied, an index (preferably UNIQUE) with the AUTO_INCREMENT column as the first
+ // column, has to exists.
+ //
+ // TODO: Only add the column, if there is not an already existing index that has the AUTO_INCREMENT column as the first index.
+ // We should also monitor all related operations, to remove the index, if it is not needed anymore.
+ // Check/test, whether this does not only apply to primary keys, but also to other (alternate) ones as well, or is
+ // completely independent of keys, and really just applies to any AUTO_INCREMENT column.
+ // Also, move handling to conventions.
+ if (operation.PrimaryKey is { Columns.Length: > 1 } primaryKey &&
+ primaryKey.Columns[0] is var firstPrimaryKeyColumnName &&
+ operation.Columns.Single(c => c.Name == firstPrimaryKeyColumnName) is var firstPrimaryKeyColumn &&
+ operation.Columns.FirstOrDefault(c => c[MySqlAnnotationNames.ValueGenerationStrategy] is MySqlValueGenerationStrategy.IdentityColumn) is { } autoIncrementColumn &&
+ operation.Columns.Contains(autoIncrementColumn) &&
+ autoIncrementColumn != firstPrimaryKeyColumn)
+ {
+ operation.UniqueConstraints.Add(
+ new AddUniqueConstraintOperation
+ {
+ Schema = operation.PrimaryKey.Schema,
+ Table = operation.PrimaryKey.Table,
+ Columns = [autoIncrementColumn.Name],
+ });
+ }
+
base.Generate(operation, model, builder, false);
var tableOptions = new List<(string, string)>();
@@ -301,17 +326,23 @@ protected override void Generate(AlterTableOperation operation, IModel model, Mi
}
else
{
+ var collationColumnName = _options.ServerVersion.Supports.CollationCharacterSetApplicabilityWithFullCollationNameColumn
+ ? "FULL_COLLATION_NAME"
+ : "COLLATION_NAME";
+
// The charset (and any collation) has been reset to the default.
- var resetCharSetSql = $@"set @__pomelo_TableCharset = (
- SELECT `ccsa`.`CHARACTER_SET_NAME` as `TABLE_CHARACTER_SET`
- FROM `INFORMATION_SCHEMA`.`TABLES` as `t`
- LEFT JOIN `INFORMATION_SCHEMA`.`COLLATION_CHARACTER_SET_APPLICABILITY` as `ccsa` ON `ccsa`.`COLLATION_NAME` = `t`.`TABLE_COLLATION`
- WHERE `TABLE_SCHEMA` = SCHEMA() AND `TABLE_NAME` = {_stringTypeMapping.GenerateSqlLiteral(operation.Name)} AND `TABLE_TYPE` IN ('BASE TABLE', 'VIEW'));
+ var resetCharSetSql = $"""
+set @__pomelo_TableCharset = (
+SELECT `ccsa`.`CHARACTER_SET_NAME` as `TABLE_CHARACTER_SET`
+FROM `INFORMATION_SCHEMA`.`TABLES` as `t`
+LEFT JOIN `INFORMATION_SCHEMA`.`COLLATION_CHARACTER_SET_APPLICABILITY` as `ccsa` ON `ccsa`.`{collationColumnName}` = `t`.`TABLE_COLLATION`
+WHERE `TABLE_SCHEMA` = SCHEMA() AND `TABLE_NAME` = {_stringTypeMapping.GenerateSqlLiteral(operation.Name)} AND `TABLE_TYPE` IN ('BASE TABLE', 'VIEW'));
SET @__pomelo_SqlExpr = CONCAT('ALTER TABLE {Dependencies.SqlGenerationHelper.DelimitIdentifier(operation.Name)} CHARACTER SET = ', @__pomelo_TableCharset, ';');
PREPARE __pomelo_SqlExprExecute FROM @__pomelo_SqlExpr;
EXECUTE __pomelo_SqlExprExecute;
-DEALLOCATE PREPARE __pomelo_SqlExprExecute;";
+DEALLOCATE PREPARE __pomelo_SqlExprExecute;
+""";
builder.AppendLine(resetCharSetSql);
EndStatement(builder);
@@ -1029,49 +1060,43 @@ protected override void Generate(
EndStatement(builder);
}
- ///
- /// Generates a SQL fragment configuring a sequence with the given options.
- ///
- /// The schema that contains the sequence, or to use the default schema.
- /// The sequence name.
- /// The sequence options.
- /// The target model which may be if the operations exist without a model.
- /// The command builder to use to add the SQL fragment.
protected override void SequenceOptions(
string schema,
string name,
SequenceOperation operation,
IModel model,
- MigrationCommandListBuilder builder)
+ MigrationCommandListBuilder builder,
+ bool forAlter)
{
- Check.NotEmpty(name, nameof(name));
- Check.NotNull(operation, nameof(operation));
- Check.NotNull(builder, nameof(builder));
+ var intTypeMapping = Dependencies.TypeMappingSource.GetMapping(typeof(int));
+ var longTypeMapping = Dependencies.TypeMappingSource.GetMapping(typeof(long));
builder
.Append(" INCREMENT BY ")
- .Append(IntegerConstant(operation.IncrementBy));
+ .Append(intTypeMapping.GenerateSqlLiteral(operation.IncrementBy));
- if (operation.MinValue.HasValue)
+ if (operation.MinValue != null)
{
builder
.Append(" MINVALUE ")
- .Append(IntegerConstant(operation.MinValue.Value));
+ .Append(longTypeMapping.GenerateSqlLiteral(operation.MinValue));
}
- else
+ else if (forAlter)
{
- builder.Append(" NO MINVALUE");
+ builder
+ .Append(" NO MINVALUE");
}
- if (operation.MaxValue.HasValue)
+ if (operation.MaxValue != null)
{
builder
.Append(" MAXVALUE ")
- .Append(IntegerConstant(operation.MaxValue.Value));
+ .Append(longTypeMapping.GenerateSqlLiteral(operation.MaxValue));
}
- else
+ else if (forAlter)
{
- builder.Append(" NO MAXVALUE");
+ builder
+ .Append(" NO MAXVALUE");
}
builder.Append(operation.IsCyclic ? " CYCLE" : " NOCYCLE");
@@ -1459,47 +1484,16 @@ protected override void CreateTablePrimaryKeyConstraint(
[CanBeNull] IModel model,
[NotNull] MigrationCommandListBuilder builder)
{
- Check.NotNull(operation, nameof(operation));
- Check.NotNull(builder, nameof(builder));
-
- var primaryKey = operation.PrimaryKey;
- if (primaryKey != null)
- {
- builder.AppendLine(",");
-
- // MySQL InnoDB has the requirement, that an AUTO_INCREMENT column has to be the first
- // column participating in an index.
-
- var sortedColumnNames = primaryKey.Columns.Length > 1
- ? primaryKey.Columns
- .Select(columnName => operation.Columns.First(co => co.Name == columnName))
- .OrderBy(co => co[MySqlAnnotationNames.ValueGenerationStrategy] is MySqlValueGenerationStrategy generationStrategy
- && generationStrategy == MySqlValueGenerationStrategy.IdentityColumn
- ? 0
- : 1)
- .Select(co => co.Name)
- .ToArray()
- : primaryKey.Columns;
-
- var sortedPrimaryKey = new AddPrimaryKeyOperation()
- {
- Schema = primaryKey.Schema,
- Table = primaryKey.Table,
- Name = primaryKey.Name,
- Columns = sortedColumnNames,
- IsDestructiveChange = primaryKey.IsDestructiveChange,
- };
-
- foreach (var annotation in primaryKey.GetAnnotations())
- {
- sortedPrimaryKey[annotation.Name] = annotation.Value;
- }
-
- PrimaryKeyConstraint(
- sortedPrimaryKey,
- model,
- builder);
- }
+ // We used to move an AUTO_INCREMENT column to the first position in a primary key, if the PK was a compound key and the column
+ // was not in the first position. We did this to satisfy InnoDB.
+ // However, this is technically an inaccuracy, and leads to incompatible FK -> PK mappings in MySQL 8.4.
+ // We will therefore reverse that behavior to leaving the key order unchanged again.
+ // This will lead to two issues:
+ // - Migrations that upgrade vom Pomelo < 9.0 to Pomelo 9.0 will not include this change automatically, because the model
+ // never changed (we only made the change (before and now) here in MySqlMigrationsSqlGenerator).
+ // - There now needs to be an index for those cases, that contains the AUTO_INCREMENT column as its first column.
+
+ base.CreateTablePrimaryKeyConstraint(operation, model, builder);
}
protected override void PrimaryKeyConstraint(
@@ -1679,7 +1673,7 @@ protected override void IndexTraits(MigrationOperation operation, IModel model,
}
}
- protected override void IndexOptions(CreateIndexOperation operation, IModel model, MigrationCommandListBuilder builder)
+ protected override void IndexOptions(MigrationOperation operation, IModel model, MigrationCommandListBuilder builder)
{
// The base implementation supports index filters in form of a WHERE clause.
// This is not supported by MySQL, so we don't call it here.
diff --git a/src/EFCore.MySql/Query/ExpressionTranslators/Internal/MySqlJsonDbFunctionsTranslator.cs b/src/EFCore.MySql/Query/ExpressionTranslators/Internal/MySqlJsonDbFunctionsTranslator.cs
index 81b77fa1d..0e4e4d73e 100644
--- a/src/EFCore.MySql/Query/ExpressionTranslators/Internal/MySqlJsonDbFunctionsTranslator.cs
+++ b/src/EFCore.MySql/Query/ExpressionTranslators/Internal/MySqlJsonDbFunctionsTranslator.cs
@@ -127,9 +127,10 @@ public virtual SqlExpression Translate(
.Append(_sqlExpressionFactory.Constant("one"))
.Append(args[1])
.AppendIfTrue(
- args.Length >= 3, () => args.Length >= 4
+ args.Length >= 3,
+ () => args.Length >= 4
? args[3]
- : _sqlExpressionFactory.Constant(null, RelationalTypeMapping.NullMapping))
+ : _sqlExpressionFactory.Constant(null, typeof(string)))
.AppendIfTrue(args.Length >= 3, () => args[2]),
typeof(bool),
null,
diff --git a/src/EFCore.MySql/Query/ExpressionTranslators/Internal/MySqlStringComparisonMethodTranslator.cs b/src/EFCore.MySql/Query/ExpressionTranslators/Internal/MySqlStringComparisonMethodTranslator.cs
index 3ee77e601..b11b50611 100644
--- a/src/EFCore.MySql/Query/ExpressionTranslators/Internal/MySqlStringComparisonMethodTranslator.cs
+++ b/src/EFCore.MySql/Query/ExpressionTranslators/Internal/MySqlStringComparisonMethodTranslator.cs
@@ -409,7 +409,7 @@ private SqlExpression MakeStartsWithEndsWithExpressionImpl(
// in C# and send a simple LIKE.
return constantPrefixSuffixExpression.Value switch
{
- null => _sqlExpressionFactory.Like(targetTransform(target), _sqlExpressionFactory.Constant(null, stringTypeMapping)),
+ null => _sqlExpressionFactory.Like(targetTransform(target), _sqlExpressionFactory.Constant(null, typeof(string), stringTypeMapping)),
"" => _sqlExpressionFactory.Like(targetTransform(target), _sqlExpressionFactory.Constant("%")),
string s => _sqlExpressionFactory.Like(
targetTransform(target),
@@ -472,7 +472,7 @@ private SqlExpression MakeContainsExpressionImpl(
// in C# and send a simple LIKE.
return constantPatternExpression.Value switch
{
- null => _sqlExpressionFactory.Like(targetTransform(target), _sqlExpressionFactory.Constant(null, stringTypeMapping)),
+ null => _sqlExpressionFactory.Like(targetTransform(target), _sqlExpressionFactory.Constant(null, typeof(string), stringTypeMapping)),
"" => _sqlExpressionFactory.Like(targetTransform(target), _sqlExpressionFactory.Constant("%")),
string s => _sqlExpressionFactory.Like(
targetTransform(target),
diff --git a/src/EFCore.MySql/Query/ExpressionVisitors/Internal/BitwiseOperationReturnTypeCorrectingExpressionVisitor.cs b/src/EFCore.MySql/Query/ExpressionVisitors/Internal/BitwiseOperationReturnTypeCorrectingExpressionVisitor.cs
new file mode 100644
index 000000000..85adbc399
--- /dev/null
+++ b/src/EFCore.MySql/Query/ExpressionVisitors/Internal/BitwiseOperationReturnTypeCorrectingExpressionVisitor.cs
@@ -0,0 +1,87 @@
+// Copyright (c) Pomelo Foundation. All rights reserved.
+// Licensed under the MIT. See LICENSE in the project root for license information.
+
+using System.Linq.Expressions;
+using Microsoft.EntityFrameworkCore.Query;
+using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
+using Pomelo.EntityFrameworkCore.MySql.Query.Internal;
+
+namespace Pomelo.EntityFrameworkCore.MySql.Query.ExpressionVisitors.Internal;
+
+///
+/// MySQL implicitly casts numbers used in all bitwise operations to BIGINT UNSIGNED.
+/// Bitwise operations are:
+///
+///
+/// Operator
+/// Description
+///
+/// -
+/// &
+/// Bitwise AND
+///
+/// -
+/// >>
+/// Right shift
+///
+/// -
+/// <<
+/// Left shift
+///
+/// -
+/// ^
+/// Bitwise OR
+///
+/// -
+/// ~
+/// Bitwise inversion
+///
+///
+/// We need to cast them back to their expected type.
+///
+public class BitwiseOperationReturnTypeCorrectingExpressionVisitor : ExpressionVisitor
+{
+ private readonly MySqlSqlExpressionFactory _sqlExpressionFactory;
+
+ public BitwiseOperationReturnTypeCorrectingExpressionVisitor(MySqlSqlExpressionFactory sqlExpressionFactory)
+ {
+ _sqlExpressionFactory = sqlExpressionFactory;
+ }
+
+ protected override Expression VisitExtension(Expression extensionExpression)
+ => extensionExpression switch
+ {
+ SqlUnaryExpression unaryExpression => VisitUnary(unaryExpression),
+ SqlBinaryExpression binaryExpression => VisitBinary(binaryExpression),
+ ShapedQueryExpression shapedQueryExpression => shapedQueryExpression.UpdateQueryExpression(Visit(shapedQueryExpression.QueryExpression)),
+ _ => base.VisitExtension(extensionExpression)
+ };
+
+ protected virtual Expression VisitUnary(SqlUnaryExpression sqlUnaryExpression)
+ => base.VisitExtension(sqlUnaryExpression) is var visitedExpression &&
+ visitedExpression is SqlUnaryExpression { OperatorType: ExpressionType.Not } visitedSqlUnaryExpression &&
+ visitedSqlUnaryExpression.Type != typeof(bool)
+ ? _sqlExpressionFactory.Convert(
+ visitedSqlUnaryExpression,
+ visitedSqlUnaryExpression.Type,
+ visitedSqlUnaryExpression.TypeMapping)
+ : visitedExpression;
+
+ protected virtual Expression VisitBinary(SqlBinaryExpression sqlBinaryExpression)
+ => base.VisitExtension(sqlBinaryExpression) is var visitedExpression &&
+ visitedExpression is SqlBinaryExpression
+ {
+ OperatorType: ExpressionType.And
+ or ExpressionType.RightShift
+ or ExpressionType.LeftShift
+ or ExpressionType.ExclusiveOr
+ or ExpressionType.Or
+ or ExpressionType.Not
+ } visitedSqlBinaryExpression &&
+ visitedSqlBinaryExpression.Type != typeof(bool)
+ ? _sqlExpressionFactory.Convert(
+ visitedSqlBinaryExpression,
+ visitedSqlBinaryExpression.Type,
+ visitedSqlBinaryExpression.TypeMapping)
+ : visitedExpression;
+}
diff --git a/src/EFCore.MySql/Query/ExpressionVisitors/Internal/MySqlBoolOptimizingExpressionVisitor.cs b/src/EFCore.MySql/Query/ExpressionVisitors/Internal/MySqlBoolOptimizingExpressionVisitor.cs
index 2ca66b9cc..d3a08bd9e 100644
--- a/src/EFCore.MySql/Query/ExpressionVisitors/Internal/MySqlBoolOptimizingExpressionVisitor.cs
+++ b/src/EFCore.MySql/Query/ExpressionVisitors/Internal/MySqlBoolOptimizingExpressionVisitor.cs
@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics;
using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
@@ -244,7 +245,7 @@ protected override Expression VisitSelect(SelectExpression selectExpression)
return changed
? selectExpression.Update(
- projections, tables, predicate, groupBy, havingExpression, orderings, limit, offset)
+ tables, predicate, groupBy, havingExpression, projections, orderings, offset, limit)
: selectExpression;
}
@@ -671,14 +672,25 @@ protected override Expression VisitValues(ValuesExpression valuesExpression)
var parentOptimize = _optimize;
_optimize = false;
- var rowValues = new RowValueExpression[valuesExpression.RowValues.Count];
- for (var i = 0; i < rowValues.Length; i++)
+ switch (valuesExpression)
{
- rowValues[i] = (RowValueExpression)Visit(valuesExpression.RowValues[i]);
- }
+ case { RowValues: not null }:
+ var rowValues = new RowValueExpression[valuesExpression.RowValues!.Count];
+ for (var i = 0; i < rowValues.Length; i++)
+ {
+ rowValues[i] = (RowValueExpression)Visit(valuesExpression.RowValues[i]);
+ }
+ _optimize = parentOptimize;
+ return valuesExpression.Update(rowValues);
- _optimize = parentOptimize;
- return valuesExpression.Update(rowValues);
+ case { ValuesParameter: not null }:
+ var valuesParameter = (SqlParameterExpression)Visit(valuesExpression.ValuesParameter);
+ _optimize = parentOptimize;
+ return valuesExpression.Update(valuesParameter);
+
+ default:
+ throw new UnreachableException();
+ }
}
}
}
diff --git a/src/EFCore.MySql/Query/ExpressionVisitors/Internal/MySqlHavingExpressionVisitor.cs b/src/EFCore.MySql/Query/ExpressionVisitors/Internal/MySqlHavingExpressionVisitor.cs
index b22dd1bb8..a6ca37788 100644
--- a/src/EFCore.MySql/Query/ExpressionVisitors/Internal/MySqlHavingExpressionVisitor.cs
+++ b/src/EFCore.MySql/Query/ExpressionVisitors/Internal/MySqlHavingExpressionVisitor.cs
@@ -8,81 +8,127 @@
using Pomelo.EntityFrameworkCore.MySql.Query.Expressions.Internal;
using Pomelo.EntityFrameworkCore.MySql.Query.Internal;
-namespace Pomelo.EntityFrameworkCore.MySql.Query.ExpressionVisitors.Internal
+namespace Pomelo.EntityFrameworkCore.MySql.Query.ExpressionVisitors.Internal;
+
+///
+/// MySQL & MariaDB currently do not support complex expressions in HAVING clauses (e.g. function calls).
+/// Instead, they want you to reference SELECT aliases for those expressions in the HAVING clause.
+/// See https://bugs.mysql.com/bug.php?id=103961
+/// This is only an issue for HAVING expressions that do not contain any aggregate functions.
+///
+public class MySqlHavingExpressionVisitor : ExpressionVisitor
{
- public class MySqlHavingExpressionVisitor : ExpressionVisitor
+ private readonly MySqlSqlExpressionFactory _sqlExpressionFactory;
+ private readonly MySqlContainsAggregateFunctionExpressionVisitor _containsAggregateFunctionExpressionVisitor;
+ private bool _usePrePostprocessorMode;
+
+ public MySqlHavingExpressionVisitor(MySqlSqlExpressionFactory sqlExpressionFactory)
+ {
+ _sqlExpressionFactory = sqlExpressionFactory;
+ _containsAggregateFunctionExpressionVisitor = new MySqlContainsAggregateFunctionExpressionVisitor();
+ }
+
+ public virtual Expression Process(Expression expression, bool usePrePostprocessorMode)
{
- private readonly MySqlSqlExpressionFactory _sqlExpressionFactory;
- private MySqlContainsAggregateFunctionExpressionVisitor _containsAggregateFunctionExpressionVisitor;
+ _usePrePostprocessorMode = usePrePostprocessorMode;
+ return Visit(expression);
+ }
- public MySqlHavingExpressionVisitor(MySqlSqlExpressionFactory sqlExpressionFactory)
+ protected override Expression VisitExtension(Expression extensionExpression)
+ => extensionExpression switch
{
- _sqlExpressionFactory = sqlExpressionFactory;
+ SelectExpression selectExpression => VisitSelect(selectExpression),
+ ShapedQueryExpression shapedQueryExpression => VisitShapedQuery(shapedQueryExpression),
+ RelationalGroupByShaperExpression relationalGroupByShaperExpression => VisitRelationalGroupByShaper(
+ relationalGroupByShaperExpression),
+ _ => base.VisitExtension(extensionExpression)
+ };
+
+ private Expression VisitRelationalGroupByShaper(RelationalGroupByShaperExpression relationalGroupByShaperExpression)
+ {
+ if (_usePrePostprocessorMode)
+ {
+ Visit(relationalGroupByShaperExpression.KeySelector);
+ Visit(relationalGroupByShaperExpression.ElementSelector);
+ Visit(relationalGroupByShaperExpression.GroupingEnumerable);
+
+ return relationalGroupByShaperExpression;
}
- protected override Expression VisitExtension(Expression extensionExpression)
- => extensionExpression switch
- {
- SelectExpression selectExpression => VisitSelect(selectExpression),
- ShapedQueryExpression shapedQueryExpression => shapedQueryExpression.Update(
- Visit(shapedQueryExpression.QueryExpression), Visit(shapedQueryExpression.ShaperExpression)),
- _ => base.VisitExtension(extensionExpression)
- };
+ return base.VisitExtension(relationalGroupByShaperExpression);
+ }
+
+ private ShapedQueryExpression VisitShapedQuery(ShapedQueryExpression shapedQueryExpression)
+ {
+ if (_usePrePostprocessorMode)
+ {
+ Visit(shapedQueryExpression.QueryExpression);
+ Visit(shapedQueryExpression.ShaperExpression);
- protected virtual Expression VisitSelect(SelectExpression selectExpression)
+ return shapedQueryExpression;
+ }
+
+ return shapedQueryExpression.Update(
+ Visit(shapedQueryExpression.QueryExpression),
+ Visit(shapedQueryExpression.ShaperExpression));
+ }
+
+ protected virtual Expression VisitSelect(SelectExpression selectExpression)
+ {
+ selectExpression = (SelectExpression)base.VisitExtension(selectExpression);
+
+ var havingExpression = selectExpression.Having;
+
+ if (HasHavingExpressionWithoutAggregateFunction(havingExpression))
{
- // MySQL & MariaDB currently do not support complex expressions in HAVING clauses (e.g. function calls).
- // Instead, they want you to reference SELECT aliases for those expressions in the HAVING clause.
- // See https://bugs.mysql.com/bug.php?id=103961
- // This is only an issue for HAVING expressions that do not contain any aggregate functions.
- var havingExpression = selectExpression.Having;
- if (havingExpression is not null &&
- havingExpression is not SqlConstantExpression &&
- havingExpression is not MySqlColumnAliasReferenceExpression)
+ if (_usePrePostprocessorMode)
{
- _containsAggregateFunctionExpressionVisitor ??= new MySqlContainsAggregateFunctionExpressionVisitor();
- if (!_containsAggregateFunctionExpressionVisitor.ProcessUntilSelect(havingExpression))
- {
- selectExpression.PushdownIntoSubquery();
- var subQuery = (SelectExpression) selectExpression.Tables.Single();
-
- var projectionIndex = subQuery.AddToProjection(havingExpression);
- var alias = subQuery.Projection[projectionIndex].Alias;
-
- var columnAliasReferenceExpression = _sqlExpressionFactory.ColumnAliasReference(
- alias,
- havingExpression,
- havingExpression.Type,
- havingExpression.TypeMapping);
-
- // Having expressions, not containing an aggregate function, need to be part of the GROUP BY clause, because they now also
- // appear as part of the SELECT clause.
- var groupBy = subQuery.GroupBy.ToList();
- groupBy.Add(columnAliasReferenceExpression);
-
- subQuery = subQuery.Update(
- subQuery.Projection,
- subQuery.Tables,
- subQuery.Predicate,
- groupBy,
- columnAliasReferenceExpression,
- subQuery.Orderings,
- subQuery.Limit,
- subQuery.Offset);
-
- selectExpression = selectExpression.Update(
- selectExpression.Projection,
- new[] {subQuery},
- selectExpression.Predicate,
- selectExpression.GroupBy,
- selectExpression.Having,
- selectExpression.Orderings,
- selectExpression.Limit,
- selectExpression.Offset);
- }
+ // This part needs to run before `RelationalQueryTranslationPostprocessor.Process()` is called, so that the
+ // `SelectExpression` is still mutable, and we can call `SelectExpression.PushdownIntoSubquery()`.
+
+ selectExpression.PushdownIntoSubquery();
+
+ // Paradoxically, it seems quite complicated to change the subquery, as long as the outer query is still mutable.
+ // We postpone that work for later, when the outer query is immutable, and we simply use the normal expression visitor
+ // update process.
}
+ else
+ {
+ // This part needs to run after `RelationalQueryTranslationPostprocessor.Process()` is called, so that the
+ // `SelectExpression` is already immutable, and we can simply update the select subquery.
+
+ var projectionIndex = selectExpression.AddToProjection(havingExpression!);
+ var alias = selectExpression.Projection[projectionIndex].Alias;
+
+ var columnAliasReferenceExpression = _sqlExpressionFactory.ColumnAliasReference(
+ alias,
+ havingExpression,
+ havingExpression.Type,
+ havingExpression.TypeMapping);
- return base.VisitExtension(selectExpression);
+ // Having expressions, not containing an aggregate function, need to be part of the GROUP BY clause, because they now
+ // also appear as part of the SELECT clause.
+ selectExpression = selectExpression.Update(
+ selectExpression.Tables,
+ selectExpression.Predicate,
+ selectExpression.GroupBy.Append(columnAliasReferenceExpression).ToList(),
+ having: columnAliasReferenceExpression,
+ selectExpression.Projection,
+ selectExpression.Orderings,
+ selectExpression.Offset,
+ selectExpression.Limit);
+ }
}
+
+ return selectExpression;
}
+
+ ///
+ /// Backed by `EFCore.MySql.Tests/Behaviors/HavingBehavior.cs`.
+ ///
+ private bool HasHavingExpressionWithoutAggregateFunction(SqlExpression havingExpression)
+ => havingExpression is not null
+ and not SqlConstantExpression
+ and not MySqlColumnAliasReferenceExpression &&
+ !_containsAggregateFunctionExpressionVisitor.ProcessUntilSelect(havingExpression);
}
diff --git a/src/EFCore.MySql/Query/ExpressionVisitors/Internal/MySqlNonWorkingHavingExpressionVisitor.cs b/src/EFCore.MySql/Query/ExpressionVisitors/Internal/MySqlNonWorkingHavingExpressionVisitor.cs
new file mode 100644
index 000000000..de79b96c9
--- /dev/null
+++ b/src/EFCore.MySql/Query/ExpressionVisitors/Internal/MySqlNonWorkingHavingExpressionVisitor.cs
@@ -0,0 +1,233 @@
+// Copyright (c) Pomelo Foundation. All rights reserved.
+// Licensed under the MIT. See LICENSE in the project root for license information.
+
+using System.Linq;
+using System.Linq.Expressions;
+using Microsoft.EntityFrameworkCore.Query;
+using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
+using Pomelo.EntityFrameworkCore.MySql.Query.Expressions.Internal;
+using Pomelo.EntityFrameworkCore.MySql.Query.Internal;
+
+namespace Pomelo.EntityFrameworkCore.MySql.Query.ExpressionVisitors.Internal
+{
+ // TODO: 9.0
+ // Remove from codebase.
+ public class MySqlNonWorkingHavingExpressionVisitor : ExpressionVisitor
+ {
+ private readonly MySqlSqlExpressionFactory _sqlExpressionFactory;
+ private readonly SqlAliasManager _sqlAliasManager;
+ private MySqlContainsAggregateFunctionExpressionVisitor _containsAggregateFunctionExpressionVisitor;
+
+ public MySqlNonWorkingHavingExpressionVisitor(MySqlSqlExpressionFactory sqlExpressionFactory, SqlAliasManager sqlAliasManager)
+ {
+ _sqlExpressionFactory = sqlExpressionFactory;
+ _sqlAliasManager = sqlAliasManager;
+ }
+
+ protected override Expression VisitExtension(Expression extensionExpression)
+ => extensionExpression switch
+ {
+ SelectExpression selectExpression => VisitSelectMutable(selectExpression),
+ // SelectExpression selectExpression => VisitSelectImmutable(selectExpression),
+ ShapedQueryExpression shapedQueryExpression => VisitShapedQuery(shapedQueryExpression),
+ _ => base.VisitExtension(extensionExpression)
+ };
+
+ private ShapedQueryExpression VisitShapedQuery(ShapedQueryExpression shapedQueryExpression)
+ => shapedQueryExpression.Update(
+ (SelectExpression)Visit(shapedQueryExpression.QueryExpression),
+ Visit(shapedQueryExpression.ShaperExpression));
+
+ ///
+ /// This might work, if we would know if the outer query is supposed to be mutable or not (which we cannot directly find out
+ /// because `SelectExpression.IsMutable` is internal) and if we could copy or recreate the projection mappings of the outer query.
+ ///
+ protected virtual Expression VisitSelectMutable(SelectExpression selectExpression)
+ {
+ // MySQL & MariaDB currently do not support complex expressions in HAVING clauses (e.g. function calls).
+ // Instead, they want you to reference SELECT aliases for those expressions in the HAVING clause.
+ // See https://bugs.mysql.com/bug.php?id=103961
+ // This is only an issue for HAVING expressions that do not contain any aggregate functions.
+ var havingExpression = selectExpression.Having;
+ if (havingExpression is not null &&
+ havingExpression is not SqlConstantExpression &&
+ havingExpression is not MySqlColumnAliasReferenceExpression)
+ {
+ _containsAggregateFunctionExpressionVisitor ??= new MySqlContainsAggregateFunctionExpressionVisitor();
+ if (!_containsAggregateFunctionExpressionVisitor.ProcessUntilSelect(havingExpression))
+ {
+ var newSelectExpression = selectExpression.Clone();
+
+ newSelectExpression.PushdownIntoSubquery();
+
+ var subQuery = (SelectExpression)newSelectExpression.Tables.Single();
+ var projectionIndex = subQuery.AddToProjection(havingExpression);
+ var alias = subQuery.Projection[projectionIndex].Alias;
+
+ var columnAliasReferenceExpression = _sqlExpressionFactory.ColumnAliasReference(
+ alias,
+ havingExpression,
+ havingExpression.Type,
+ havingExpression.TypeMapping);
+
+ // Having expressions, not containing an aggregate function, need to be part of the GROUP BY clause, because they now also
+ // appear as part of the SELECT clause.
+ var groupBy = subQuery.GroupBy.ToList();
+ groupBy.Add(columnAliasReferenceExpression);
+
+ subQuery = new SelectExpression(
+ subQuery.Alias,
+ subQuery.Tables.ToList(),
+ subQuery.Predicate,
+ groupBy,
+ columnAliasReferenceExpression,
+ subQuery.Projection.ToList(),
+ subQuery.IsDistinct,
+ subQuery.Orderings.ToList(),
+ subQuery.Offset,
+ subQuery.Limit,
+ subQuery.Tags,
+ subQuery.GetAnnotations().ToDictionary(a => a.Name, a => a),
+ _sqlAliasManager,
+ isMutable: false
+ );
+
+ newSelectExpression = new SelectExpression(
+ newSelectExpression.Alias,
+ [subQuery],
+ newSelectExpression.Predicate,
+ newSelectExpression.GroupBy.ToList(),
+ newSelectExpression.Having,
+ projections: [],
+ newSelectExpression.IsDistinct,
+ newSelectExpression.Orderings.ToList(),
+ newSelectExpression.Offset,
+ newSelectExpression.Limit,
+ newSelectExpression.Tags,
+ newSelectExpression.GetAnnotations().ToDictionary(a => a.Name, a => a),
+ _sqlAliasManager,
+ isMutable: true
+ );
+
+ //
+ // UNSOLVED: Somehow recreate projection mappings here.
+ //
+
+ selectExpression = newSelectExpression;
+ }
+ }
+
+ return base.VisitExtension(selectExpression);
+ }
+
+ ///
+ /// This basically needs to reimplement `SelectExpression.PushdownIntoSubquery()`, which we are definitely not going to do.
+ ///
+ protected virtual Expression VisitSelectImmutable(SelectExpression selectExpression)
+ {
+ // MySQL & MariaDB currently do not support complex expressions in HAVING clauses (e.g. function calls).
+ // Instead, they want you to reference SELECT aliases for those expressions in the HAVING clause.
+ // See https://bugs.mysql.com/bug.php?id=103961
+ // This is only an issue for HAVING expressions that do not contain any aggregate functions.
+ var havingExpression = selectExpression.Having;
+ if (havingExpression is not null &&
+ havingExpression is not SqlConstantExpression &&
+ havingExpression is not MySqlColumnAliasReferenceExpression)
+ {
+ _containsAggregateFunctionExpressionVisitor ??= new MySqlContainsAggregateFunctionExpressionVisitor();
+ if (!_containsAggregateFunctionExpressionVisitor.ProcessUntilSelect(havingExpression))
+ {
+ var subquery = selectExpression.Clone();
+ subquery.ReplaceProjection([]);
+
+ var alias = "having";
+ var havingProjectionExpression = new ProjectionExpression(havingExpression, alias);
+ var columnAliasReferenceExpression = _sqlExpressionFactory.ColumnAliasReference(
+ alias,
+ havingExpression,
+ havingExpression.Type,
+ havingExpression.TypeMapping);
+
+ // Having expressions, not containing an aggregate function, need to be part of the GROUP BY clause, because they now also
+ // appear as part of the SELECT clause.
+ subquery = subquery.Update(
+ subquery.Tables,
+ subquery.Predicate,
+ subquery.GroupBy.Append(columnAliasReferenceExpression).ToList(),
+ columnAliasReferenceExpression,
+ subquery.Projection.Append(havingProjectionExpression).ToList(),
+ subquery.Limit is not null || subquery.Offset is not null
+ ? subquery.Orderings
+ : [],
+ subquery.Offset, // Offset/limit parameters got switched around between EF Core 8 and 9 for no good reason.
+ subquery.Limit);
+
+
+ var outerSelectOrderings = selectExpression.Orderings;
+
+ // foreach (var ordering in subquery.Orderings)
+ // {
+ // var orderingExpression = ordering.Expression;
+ // if (liftOrderings && projectionMap.TryGetValue(orderingExpression, out var outerColumn))
+ // {
+ // _orderings.Add(ordering.Update(outerColumn));
+ // }
+ // else if (liftOrderings
+ // && (!IsDistinct
+ // && GroupBy.Count == 0
+ // || GroupBy.Contains(orderingExpression)))
+ // {
+ // _orderings.Add(
+ // ordering.Update(
+ // subquery.GenerateOuterColumn(subqueryAlias, orderingExpression)));
+ // }
+ // else
+ // {
+ // _orderings.Clear();
+ // break;
+ // }
+ // }
+
+
+ selectExpression = selectExpression.Update(
+ [subquery],
+ selectExpression.Predicate,
+ groupBy: [],
+ having: null,
+ subquery.Projection/*.Select(p => new ProjectionExpression(new ColumnExpression(p.Alias, subquery.Alias, p.Type, null)))*/,
+ outerSelectOrderings,
+ null,
+ null);
+ }
+ }
+
+ return base.VisitExtension(selectExpression);
+ }
+
+ // private ColumnExpression GenerateOuterColumn(
+ // SelectExpression subquery,
+ // string tableAlias,
+ // SqlExpression projection/*,
+ // string columnAlias = null*/)
+ // {
+ // // TODO: Add check if we can add projection in subquery to generate out column
+ // // Subquery having Distinct or GroupBy can block it.
+ // var index = subquery.AddToProjection(projection);
+ // var projectionExpression = subquery.Projection[index];
+ // return CreateColumnExpression(projectionExpression, tableAlias);
+ // }
+ //
+ // private static ColumnExpression CreateColumnExpression(ProjectionExpression subqueryProjection, string tableAlias)
+ // => new(
+ // subqueryProjection.Alias,
+ // tableAlias,
+ // subqueryProjection.Type,
+ // subqueryProjection.Expression.TypeMapping!,
+ // subqueryProjection.Expression switch
+ // {
+ // ColumnExpression columnExpression => columnExpression.IsNullable,
+ // SqlConstantExpression sqlConstantExpression => sqlConstantExpression.Value == null,
+ // _ => true
+ // });
+ }
+}
diff --git a/src/EFCore.MySql/Query/ExpressionVisitors/Internal/MySqlParameterInliningExpressionVisitor.cs b/src/EFCore.MySql/Query/ExpressionVisitors/Internal/MySqlParameterInliningExpressionVisitor.cs
index 2d3885cc5..da9be518d 100644
--- a/src/EFCore.MySql/Query/ExpressionVisitors/Internal/MySqlParameterInliningExpressionVisitor.cs
+++ b/src/EFCore.MySql/Query/ExpressionVisitors/Internal/MySqlParameterInliningExpressionVisitor.cs
@@ -107,7 +107,7 @@ protected virtual Expression VisitSqlParameter(SqlParameterExpression sqlParamet
return new MySqlInlinedParameterExpression(
sqlParameterExpression,
- _sqlExpressionFactory.Constant(
+ (SqlConstantExpression)_sqlExpressionFactory.Constant(
_parametersValues[sqlParameterExpression.Name],
sqlParameterExpression.TypeMapping));
}
diff --git a/src/EFCore.MySql/Query/ExpressionVisitors/Internal/MySqlQuerySqlGenerator.cs b/src/EFCore.MySql/Query/ExpressionVisitors/Internal/MySqlQuerySqlGenerator.cs
index e6344f625..c86ad0e9e 100644
--- a/src/EFCore.MySql/Query/ExpressionVisitors/Internal/MySqlQuerySqlGenerator.cs
+++ b/src/EFCore.MySql/Query/ExpressionVisitors/Internal/MySqlQuerySqlGenerator.cs
@@ -400,7 +400,7 @@ protected override Expression VisitDelete(DeleteExpression deleteExpression)
}
throw new InvalidOperationException(
- RelationalStrings.ExecuteOperationWithUnsupportedOperatorInSqlGeneration(nameof(RelationalQueryableExtensions.ExecuteDelete)));
+ RelationalStrings.ExecuteOperationWithUnsupportedOperatorInSqlGeneration(nameof(EntityFrameworkQueryableExtensions.ExecuteDelete)));
}
protected override Expression VisitUpdate(UpdateExpression updateExpression)
@@ -468,7 +468,7 @@ protected override Expression VisitUpdate(UpdateExpression updateExpression)
}
throw new InvalidOperationException(
- RelationalStrings.ExecuteOperationWithUnsupportedOperatorInSqlGeneration(nameof(RelationalQueryableExtensions.ExecuteUpdate)));
+ RelationalStrings.ExecuteOperationWithUnsupportedOperatorInSqlGeneration(nameof(EntityFrameworkQueryableExtensions.ExecuteUpdate)));
}
protected override Expression VisitJsonScalar(JsonScalarExpression jsonScalarExpression)
@@ -536,6 +536,11 @@ protected override Expression VisitJsonScalar(JsonScalarExpression jsonScalarExp
protected override void GenerateValues(ValuesExpression valuesExpression)
{
+ if (valuesExpression.RowValues is null)
+ {
+ throw new UnreachableException();
+ }
+
if (_options.ServerVersion.Supports.Values ||
_options.ServerVersion.Supports.ValuesWithRows)
{
diff --git a/src/EFCore.MySql/Query/ExpressionVisitors/Internal/MySqlQueryTranslationPostprocessor.cs b/src/EFCore.MySql/Query/ExpressionVisitors/Internal/MySqlQueryTranslationPostprocessor.cs
index ed219a6ed..cb0956c07 100644
--- a/src/EFCore.MySql/Query/ExpressionVisitors/Internal/MySqlQueryTranslationPostprocessor.cs
+++ b/src/EFCore.MySql/Query/ExpressionVisitors/Internal/MySqlQueryTranslationPostprocessor.cs
@@ -27,8 +27,15 @@ public MySqlQueryTranslationPostprocessor(
public override Expression Process(Expression query)
{
+ var mySqlHavingExpressionVisitor = new MySqlHavingExpressionVisitor(_sqlExpressionFactory);
+
+ query = mySqlHavingExpressionVisitor.Process(query, usePrePostprocessorMode: true);
+
+ // Changes `SelectExpression.IsMutable` from `true` to `false`.
query = base.Process(query);
+ query = mySqlHavingExpressionVisitor.Process(query, usePrePostprocessorMode: false);
+
query = new MySqlJsonParameterExpressionVisitor(_sqlExpressionFactory, _options).Visit(query);
if (_options.ServerVersion.Supports.MySqlBug96947Workaround)
@@ -36,6 +43,8 @@ public override Expression Process(Expression query)
query = new MySqlBug96947WorkaroundExpressionVisitor(_sqlExpressionFactory).Visit(query);
}
+ query = new BitwiseOperationReturnTypeCorrectingExpressionVisitor(_sqlExpressionFactory).Visit(query);
+
return query;
}
}
diff --git a/src/EFCore.MySql/Query/ExpressionVisitors/Internal/MySqlQueryableMethodNormalizingExpressionVisitor.cs b/src/EFCore.MySql/Query/ExpressionVisitors/Internal/MySqlQueryableMethodNormalizingExpressionVisitor.cs
index bd0773c89..5b5998738 100644
--- a/src/EFCore.MySql/Query/ExpressionVisitors/Internal/MySqlQueryableMethodNormalizingExpressionVisitor.cs
+++ b/src/EFCore.MySql/Query/ExpressionVisitors/Internal/MySqlQueryableMethodNormalizingExpressionVisitor.cs
@@ -10,6 +10,10 @@
namespace Pomelo.EntityFrameworkCore.MySql.Query.ExpressionVisitors.Internal;
+// TODO: 9.0
+// Remove MySqlQueryableMethodNormalizingExpressionVisitor, MySqlBipolarExpression and
+// MySqlQueryTranslationPreprocessor.NormalizeQueryableMethod (or the whole class) and use ElementAt() directly in Json translation classes.
+
///
/// Skips normalization of array[index].Property to array.Select(e => e.Property).ElementAt(index),
/// because it messes-up our JSON-Array handling in `MySqlSqlTranslatingExpressionVisitor`.
@@ -18,7 +22,7 @@ namespace Pomelo.EntityFrameworkCore.MySql.Query.ExpressionVisitors.Internal;
public class MySqlQueryableMethodNormalizingExpressionVisitor : QueryableMethodNormalizingExpressionVisitor
{
public MySqlQueryableMethodNormalizingExpressionVisitor(QueryCompilationContext queryCompilationContext)
- : base(queryCompilationContext)
+ : base(queryCompilationContext, isEfConstantSupported: true)
{
}
@@ -75,7 +79,7 @@ IEnumerable VisitArguments(IEnumerable arguments)
{
foreach (var expression in arguments)
{
- yield return VisitExtension(expression);
+ yield return Visit(expression);
}
}
diff --git a/src/EFCore.MySql/Query/ExpressionVisitors/Internal/MySqlSqlTranslatingExpressionVisitor.cs b/src/EFCore.MySql/Query/ExpressionVisitors/Internal/MySqlSqlTranslatingExpressionVisitor.cs
index 312bfad58..b2922e35a 100644
--- a/src/EFCore.MySql/Query/ExpressionVisitors/Internal/MySqlSqlTranslatingExpressionVisitor.cs
+++ b/src/EFCore.MySql/Query/ExpressionVisitors/Internal/MySqlSqlTranslatingExpressionVisitor.cs
@@ -31,7 +31,8 @@ public class MySqlSqlTranslatingExpressionVisitor : RelationalSqlTranslatingExpr
protected static readonly MethodInfo[] NewArrayExpressionSupportMethodInfos = Array.Empty()
.Concat(typeof(MySqlDbFunctionsExtensions).GetRuntimeMethods().Where(m => m.Name is nameof(MySqlDbFunctionsExtensions.Match)
or nameof(MySqlDbFunctionsExtensions.IsMatch)))
- .Concat(typeof(string).GetRuntimeMethods().Where(m => m.Name == nameof(string.Concat)))
+ .Concat(typeof(string).GetRuntimeMethods().Where(m => m.Name is nameof(string.Concat)
+ or nameof(string.Join)))
.Where(m => m.GetParameters().Any(p => p.ParameterType.IsArray))
.ToArray();
@@ -125,21 +126,7 @@ protected override Expression VisitUnary(UnaryExpression unaryExpression)
ResetTranslationErrorDetails();
}
- var visitedExpression = base.VisitUnary(unaryExpression);
-
- if (visitedExpression is SqlUnaryExpression sqlUnaryExpression &&
- sqlUnaryExpression.OperatorType == ExpressionType.Not &&
- sqlUnaryExpression.Type != typeof(bool))
- {
- // MySQL implicitly casts numbers used in BITWISE NOT operations (~ operator) to BIGINT UNSIGNED.
- // We need to cast them back, to get the expected result.
- return _sqlExpressionFactory.Convert(
- sqlUnaryExpression,
- sqlUnaryExpression.Type,
- sqlUnaryExpression.TypeMapping);
- }
-
- return visitedExpression;
+ return base.VisitUnary(unaryExpression);
}
protected override Expression VisitBinary(BinaryExpression binaryExpression)
@@ -306,7 +293,7 @@ private Expression TranslateByteArrayElementAccess(Expression array, Expression
protected virtual Expression VisitMethodCallNewArray(NewArrayExpression newArrayExpression)
{
- // Needed for MySqlDbFunctionsExtensions.Match() and String.Concat() translation.
+ // Needed for MySqlDbFunctionsExtensions.Match(), String.Concat() and String.Join() translations.
if (newArrayExpression.Type == typeof(string[]))
{
return _sqlExpressionFactory.ComplexFunctionArgument(
@@ -316,7 +303,7 @@ protected virtual Expression VisitMethodCallNewArray(NewArrayExpression newArray
typeof(string[]));
}
- // Needed for String.Concat() translation.
+ // Needed for String.Concat() and String.Join() translations.
if (newArrayExpression.Type == typeof(object[]))
{
var typeMapping = ((MySqlStringTypeMapping)Dependencies.TypeMappingSource.GetMapping(typeof(string))).Clone(forceToString: true);
@@ -435,6 +422,20 @@ protected virtual void ResetTranslationErrorDetails()
base.Translate(Expression.Constant(0));
}
+ public override SqlExpression GenerateGreatest(IReadOnlyList expressions, Type resultType)
+ => _sqlExpressionFactory.NullableFunction(
+ "GREATEST",
+ expressions,
+ resultType,
+ true);
+
+ public override SqlExpression GenerateLeast(IReadOnlyList expressions, Type resultType)
+ => _sqlExpressionFactory.NullableFunction(
+ "LEAST",
+ expressions,
+ resultType,
+ true);
+
#region Copied from RelationalSqlTranslatingExpressionVisitor
private static Expression TryRemoveImplicitConvert(Expression expression)
diff --git a/src/EFCore.MySql/Query/Expressions/Internal/MySqlBinaryExpression.cs b/src/EFCore.MySql/Query/Expressions/Internal/MySqlBinaryExpression.cs
index c074afcad..0feba00f5 100644
--- a/src/EFCore.MySql/Query/Expressions/Internal/MySqlBinaryExpression.cs
+++ b/src/EFCore.MySql/Query/Expressions/Internal/MySqlBinaryExpression.cs
@@ -3,6 +3,7 @@
using System;
using System.Linq.Expressions;
+using System.Reflection;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
using Microsoft.EntityFrameworkCore.Storage;
@@ -27,6 +28,8 @@ public enum MySqlBinaryExpressionOperatorType
public class MySqlBinaryExpression : SqlExpression
{
+ private static ConstructorInfo _quotingConstructor;
+
public MySqlBinaryExpression(
MySqlBinaryExpressionOperatorType operatorType,
SqlExpression left,
@@ -61,6 +64,16 @@ protected override Expression VisitChildren(ExpressionVisitor visitor)
return Update(left, right);
}
+ public override Expression Quote()
+ => New(
+ _quotingConstructor ??= typeof(MySqlBinaryExpression).GetConstructor(
+ [typeof(MySqlBinaryExpressionOperatorType), typeof(SqlExpression), typeof(SqlExpression), typeof(Type), typeof(RelationalTypeMapping)])!,
+ Constant(OperatorType),
+ Left.Quote(),
+ Right.Quote(),
+ Constant(Type),
+ RelationalExpressionQuotingUtilities.QuoteTypeMapping(TypeMapping));
+
public virtual MySqlBinaryExpression Update(SqlExpression left, SqlExpression right)
=> left != Left || right != Right
? new MySqlBinaryExpression(OperatorType, left, right, Type, TypeMapping)
diff --git a/src/EFCore.MySql/Query/Expressions/Internal/MySqlCollateExpression.cs b/src/EFCore.MySql/Query/Expressions/Internal/MySqlCollateExpression.cs
index 119c8c292..3d3532cfa 100644
--- a/src/EFCore.MySql/Query/Expressions/Internal/MySqlCollateExpression.cs
+++ b/src/EFCore.MySql/Query/Expressions/Internal/MySqlCollateExpression.cs
@@ -4,6 +4,7 @@
using System;
using System.Linq;
using System.Linq.Expressions;
+using System.Reflection;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
@@ -17,6 +18,8 @@ namespace Pomelo.EntityFrameworkCore.MySql.Query.Expressions.Internal
///
public class MySqlCollateExpression : SqlExpression
{
+ private static ConstructorInfo _quotingConstructor;
+
private readonly SqlExpression _valueExpression;
private readonly string _charset;
private readonly string _collation;
@@ -77,6 +80,15 @@ protected override Expression VisitChildren(ExpressionVisitor visitor)
return Update(valueExpression);
}
+ public override Expression Quote()
+ => New(
+ _quotingConstructor ??= typeof(MySqlInlinedParameterExpression).GetConstructor(
+ [typeof(SqlExpression), typeof(string), typeof(string), typeof(RelationalTypeMapping)])!,
+ ValueExpression.Quote(),
+ Constant(Charset),
+ Constant(Collation),
+ RelationalExpressionQuotingUtilities.QuoteTypeMapping(TypeMapping));
+
public virtual MySqlCollateExpression Update(SqlExpression valueExpression)
=> valueExpression != _valueExpression &&
valueExpression != null
diff --git a/src/EFCore.MySql/Query/Expressions/Internal/MySqlColumnAliasReferenceExpression.cs b/src/EFCore.MySql/Query/Expressions/Internal/MySqlColumnAliasReferenceExpression.cs
index 70e184dc3..ecb40828a 100644
--- a/src/EFCore.MySql/Query/Expressions/Internal/MySqlColumnAliasReferenceExpression.cs
+++ b/src/EFCore.MySql/Query/Expressions/Internal/MySqlColumnAliasReferenceExpression.cs
@@ -3,6 +3,7 @@
using System;
using System.Linq.Expressions;
+using System.Reflection;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
@@ -15,6 +16,8 @@ namespace Pomelo.EntityFrameworkCore.MySql.Query.Expressions.Internal
///
public class MySqlColumnAliasReferenceExpression : SqlExpression, IEquatable
{
+ private static ConstructorInfo _quotingConstructor;
+
[NotNull]
public virtual string Alias { get; }
@@ -35,6 +38,15 @@ public MySqlColumnAliasReferenceExpression(
protected override Expression VisitChildren(ExpressionVisitor visitor)
=> this;
+ public override Expression Quote()
+ => New(
+ _quotingConstructor ??= typeof(MySqlColumnAliasReferenceExpression).GetConstructor(
+ [typeof(string), typeof(SqlExpression), typeof(Type), typeof(RelationalTypeMapping)])!,
+ Constant(Alias),
+ Expression,
+ Constant(Type),
+ RelationalExpressionQuotingUtilities.QuoteTypeMapping(TypeMapping));
+
public virtual MySqlColumnAliasReferenceExpression Update(
[NotNull] string alias,
[NotNull] SqlExpression expression)
diff --git a/src/EFCore.MySql/Query/Expressions/Internal/MySqlComplexFunctionArgumentExpression.cs b/src/EFCore.MySql/Query/Expressions/Internal/MySqlComplexFunctionArgumentExpression.cs
index 58ebb520e..92b0d359f 100644
--- a/src/EFCore.MySql/Query/Expressions/Internal/MySqlComplexFunctionArgumentExpression.cs
+++ b/src/EFCore.MySql/Query/Expressions/Internal/MySqlComplexFunctionArgumentExpression.cs
@@ -5,6 +5,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
+using System.Reflection;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Storage;
@@ -14,6 +15,8 @@ namespace Pomelo.EntityFrameworkCore.MySql.Query.Expressions.Internal
{
public class MySqlComplexFunctionArgumentExpression : SqlExpression
{
+ private static ConstructorInfo _quotingConstructor;
+
public MySqlComplexFunctionArgumentExpression(
IEnumerable argumentParts,
string delimiter,
@@ -52,6 +55,16 @@ protected override Expression VisitChildren(ExpressionVisitor visitor)
return Update(argumentParts, Delimiter);
}
+ ///
+ public override Expression Quote()
+ => New(
+ _quotingConstructor ??= typeof(MySqlColumnAliasReferenceExpression).GetConstructor(
+ [typeof(IReadOnlyList), typeof(string), typeof(Type), typeof(RelationalTypeMapping)])!,
+ NewArrayInit(typeof(SqlExpression), ArgumentParts.Select(p => p.Quote())),
+ Constant(Delimiter),
+ Constant(Type),
+ RelationalExpressionQuotingUtilities.QuoteTypeMapping(TypeMapping));
+
public virtual MySqlComplexFunctionArgumentExpression Update(IReadOnlyList argumentParts, string delimiter)
=> !argumentParts.SequenceEqual(ArgumentParts)
? new MySqlComplexFunctionArgumentExpression(argumentParts, delimiter, Type, TypeMapping)
diff --git a/src/EFCore.MySql/Query/Expressions/Internal/MySqlInlinedParameterExpression.cs b/src/EFCore.MySql/Query/Expressions/Internal/MySqlInlinedParameterExpression.cs
index 6824fa3e8..007bd8238 100644
--- a/src/EFCore.MySql/Query/Expressions/Internal/MySqlInlinedParameterExpression.cs
+++ b/src/EFCore.MySql/Query/Expressions/Internal/MySqlInlinedParameterExpression.cs
@@ -3,6 +3,7 @@
using System;
using System.Linq.Expressions;
+using System.Reflection;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
using Microsoft.EntityFrameworkCore.Utilities;
@@ -11,6 +12,8 @@ namespace Pomelo.EntityFrameworkCore.MySql.Query.Expressions.Internal;
public class MySqlInlinedParameterExpression : SqlExpression
{
+ private static ConstructorInfo _quotingConstructor;
+
public MySqlInlinedParameterExpression(
SqlParameterExpression parameterExpression,
SqlConstantExpression valueExpression)
@@ -22,7 +25,7 @@ public MySqlInlinedParameterExpression(
ValueExpression = valueExpression;
}
- public virtual Expression ParameterExpression { get; }
+ public virtual SqlParameterExpression ParameterExpression { get; }
public virtual SqlConstantExpression ValueExpression { get; }
protected override Expression VisitChildren(ExpressionVisitor visitor)
@@ -33,6 +36,14 @@ protected override Expression VisitChildren(ExpressionVisitor visitor)
return Update(parameterExpression, valueExpression);
}
+ ///
+ public override Expression Quote()
+ => New(
+ _quotingConstructor ??= typeof(MySqlInlinedParameterExpression).GetConstructor(
+ [typeof(SqlParameterExpression), typeof(SqlConstantExpression)])!,
+ ParameterExpression.Quote(),
+ ValueExpression.Quote());
+
protected override void Print(ExpressionPrinter expressionPrinter)
{
expressionPrinter.Visit(ValueExpression);
diff --git a/src/EFCore.MySql/Query/Expressions/Internal/MySqlJsonArrayIndexExpression.cs b/src/EFCore.MySql/Query/Expressions/Internal/MySqlJsonArrayIndexExpression.cs
index c3a597ab3..e97c4e7cc 100644
--- a/src/EFCore.MySql/Query/Expressions/Internal/MySqlJsonArrayIndexExpression.cs
+++ b/src/EFCore.MySql/Query/Expressions/Internal/MySqlJsonArrayIndexExpression.cs
@@ -3,6 +3,7 @@
using System;
using System.Linq.Expressions;
+using System.Reflection;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
@@ -15,6 +16,8 @@ namespace Pomelo.EntityFrameworkCore.MySql.Query.Expressions.Internal
///
public class MySqlJsonArrayIndexExpression : SqlExpression, IEquatable
{
+ private static ConstructorInfo _quotingConstructor;
+
[NotNull]
public virtual SqlExpression Expression { get; }
@@ -30,6 +33,15 @@ public MySqlJsonArrayIndexExpression(
protected override Expression VisitChildren(ExpressionVisitor visitor)
=> Update((SqlExpression)visitor.Visit(Expression));
+ ///
+ public override Expression Quote()
+ => New(
+ _quotingConstructor ??= typeof(MySqlInlinedParameterExpression).GetConstructor(
+ [typeof(SqlExpression), typeof(Type), typeof(RelationalTypeMapping)])!,
+ Expression.Quote(),
+ Constant(Type),
+ RelationalExpressionQuotingUtilities.QuoteTypeMapping(TypeMapping));
+
public virtual MySqlJsonArrayIndexExpression Update(
[NotNull] SqlExpression expression)
=> expression == Expression
diff --git a/src/EFCore.MySql/Query/Expressions/Internal/MySqlJsonTraversalExpression.cs b/src/EFCore.MySql/Query/Expressions/Internal/MySqlJsonTraversalExpression.cs
index 68aed5a0c..194507529 100644
--- a/src/EFCore.MySql/Query/Expressions/Internal/MySqlJsonTraversalExpression.cs
+++ b/src/EFCore.MySql/Query/Expressions/Internal/MySqlJsonTraversalExpression.cs
@@ -5,6 +5,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
+using System.Reflection;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
@@ -17,6 +18,8 @@ namespace Pomelo.EntityFrameworkCore.MySql.Query.Expressions.Internal
///
public class MySqlJsonTraversalExpression : SqlExpression, IEquatable
{
+ private static ConstructorInfo _quotingConstructor;
+
///
/// The JSON column.
///
@@ -67,6 +70,16 @@ protected override Expression VisitChildren(ExpressionVisitor visitor)
(SqlExpression)visitor.Visit(Expression),
Path.Select(p => (SqlExpression)visitor.Visit(p)).ToArray());
+ ///
+ public override Expression Quote()
+ => New(
+ _quotingConstructor ??= typeof(MySqlInlinedParameterExpression).GetConstructor(
+ [typeof(SqlExpression), typeof(IReadOnlyList), typeof(bool), typeof(Type), typeof(RelationalTypeMapping)])!,
+ Expression.Quote(),
+ NewArrayInit(typeof(SqlExpression), Path.Select(p => p.Quote())),
+ Constant(ReturnsText),
+ RelationalExpressionQuotingUtilities.QuoteTypeMapping(TypeMapping));
+
public virtual MySqlJsonTraversalExpression Update(
[NotNull] SqlExpression expression,
[NotNull] IReadOnlyList path)
diff --git a/src/EFCore.MySql/Query/Expressions/Internal/MySqlMatchExpression.cs b/src/EFCore.MySql/Query/Expressions/Internal/MySqlMatchExpression.cs
index 28d823edd..bef262954 100644
--- a/src/EFCore.MySql/Query/Expressions/Internal/MySqlMatchExpression.cs
+++ b/src/EFCore.MySql/Query/Expressions/Internal/MySqlMatchExpression.cs
@@ -3,6 +3,7 @@
using System;
using System.Linq.Expressions;
+using System.Reflection;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
@@ -14,6 +15,8 @@ namespace Pomelo.EntityFrameworkCore.MySql.Query.Expressions.Internal
{
public class MySqlMatchExpression : SqlExpression
{
+ private static ConstructorInfo _quotingConstructor;
+
public MySqlMatchExpression(
SqlExpression match,
SqlExpression against,
@@ -51,6 +54,16 @@ protected override Expression VisitChildren(ExpressionVisitor visitor)
return Update(match, against);
}
+ ///
+ public override Expression Quote()
+ => New(
+ _quotingConstructor ??= typeof(MySqlInlinedParameterExpression).GetConstructor(
+ [typeof(SqlExpression), typeof(SqlExpression), typeof(MySqlMatchSearchMode), typeof(RelationalTypeMapping)])!,
+ Match.Quote(),
+ Against.Quote(),
+ Constant(SearchMode),
+ RelationalExpressionQuotingUtilities.QuoteTypeMapping(TypeMapping));
+
public virtual MySqlMatchExpression Update(SqlExpression match, SqlExpression against)
=> match != Match || against != Against
? new MySqlMatchExpression(
diff --git a/src/EFCore.MySql/Query/Expressions/Internal/MySqlRegexpExpression.cs b/src/EFCore.MySql/Query/Expressions/Internal/MySqlRegexpExpression.cs
index 84d79ae51..6f7f0ceff 100644
--- a/src/EFCore.MySql/Query/Expressions/Internal/MySqlRegexpExpression.cs
+++ b/src/EFCore.MySql/Query/Expressions/Internal/MySqlRegexpExpression.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT. See LICENSE in the project root for license information.
using System.Linq.Expressions;
+using System.Reflection;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Utilities;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
@@ -13,6 +14,8 @@ namespace Pomelo.EntityFrameworkCore.MySql.Query.Expressions.Internal
{
public class MySqlRegexpExpression : SqlExpression
{
+ private static ConstructorInfo _quotingConstructor;
+
public MySqlRegexpExpression(
[NotNull] SqlExpression match,
[NotNull] SqlExpression pattern,
@@ -46,6 +49,15 @@ protected override Expression VisitChildren(ExpressionVisitor visitor)
return Update(match, pattern);
}
+ ///
+ public override Expression Quote()
+ => New(
+ _quotingConstructor ??= typeof(MySqlInlinedParameterExpression).GetConstructor(
+ [typeof(SqlExpression), typeof(SqlExpression), typeof(RelationalTypeMapping)])!,
+ Match.Quote(),
+ Pattern.Quote(),
+ RelationalExpressionQuotingUtilities.QuoteTypeMapping(TypeMapping));
+
public virtual MySqlRegexpExpression Update(SqlExpression match, SqlExpression pattern)
=> match != Match ||
pattern != Pattern
diff --git a/src/EFCore.MySql/Query/Internal/MySqlDateTimeMethodTranslator.cs b/src/EFCore.MySql/Query/Internal/MySqlDateTimeMethodTranslator.cs
index cff98cabb..4b178e61c 100644
--- a/src/EFCore.MySql/Query/Internal/MySqlDateTimeMethodTranslator.cs
+++ b/src/EFCore.MySql/Query/Internal/MySqlDateTimeMethodTranslator.cs
@@ -44,6 +44,8 @@ public class MySqlDateTimeMethodTranslator : IMethodCallTranslator
private static readonly MethodInfo _timeOnlyAddTimeSpanMethod = typeof(TimeOnly).GetRuntimeMethod(nameof(TimeOnly.Add), new[] { typeof(TimeSpan) })!;
private static readonly MethodInfo _timeOnlyIsBetweenMethod = typeof(TimeOnly).GetRuntimeMethod(nameof(TimeOnly.IsBetween), new[] { typeof(TimeOnly), typeof(TimeOnly) })!;
+ private static readonly MethodInfo _timeOnlyFromDateTimeMethod = typeof(TimeOnly).GetRuntimeMethod(nameof(TimeOnly.FromDateTime), new[] { typeof(DateTime) })!;
+ private static readonly MethodInfo _timeOnlyFromTimeSpanMethod = typeof(TimeOnly).GetRuntimeMethod(nameof(TimeOnly.FromTimeSpan), new[] { typeof(TimeSpan) })!;
private static readonly MethodInfo _dateOnlyFromDateTimeMethod = typeof(DateOnly).GetRuntimeMethod(nameof(DateOnly.FromDateTime), new[] { typeof(DateTime) })!;
private static readonly MethodInfo _dateOnlyToDateTimeMethod = typeof(DateOnly).GetRuntimeMethod(nameof(DateOnly.ToDateTime), new[] { typeof(TimeOnly) })!;
@@ -137,11 +139,31 @@ public virtual SqlExpression Translate(
_sqlExpressionFactory.GreaterThanOrEqual(instance, arguments[0]),
_sqlExpressionFactory.LessThan(instance, arguments[1]));
}
+
+ if (instance is null &&
+ arguments.Count == 1)
+ {
+ if (method == _timeOnlyFromDateTimeMethod)
+ {
+ return _sqlExpressionFactory.NullableFunction(
+ "TIME",
+ arguments,
+ typeof(TimeOnly),
+ onlyNullWhenAnyNullPropagatingArgumentIsNull: true);
+ }
+
+ if (method == _timeOnlyFromTimeSpanMethod)
+ {
+ return _sqlExpressionFactory.Convert(arguments[0], method.ReturnType);
+ }
+ }
}
if (method.DeclaringType == typeof(DateOnly))
{
- if (method == _dateOnlyFromDateTimeMethod)
+ if (method == _dateOnlyFromDateTimeMethod &&
+ instance is null &&
+ arguments.Count == 1)
{
return _sqlExpressionFactory.NullableFunction(
"DATE",
diff --git a/src/EFCore.MySql/Query/Internal/MySqlMathMethodTranslator.cs b/src/EFCore.MySql/Query/Internal/MySqlMathMethodTranslator.cs
index b2f933d4c..f59230c39 100644
--- a/src/EFCore.MySql/Query/Internal/MySqlMathMethodTranslator.cs
+++ b/src/EFCore.MySql/Query/Internal/MySqlMathMethodTranslator.cs
@@ -63,21 +63,8 @@ public class MySqlMathMethodTranslator : IMethodCallTranslator
{ typeof(Math).GetRuntimeMethod(nameof(Math.Log10), new[] { typeof(double) }), ("LOG10", false, false) },
{ typeof(MathF).GetRuntimeMethod(nameof(MathF.Log10), new[] { typeof(float) }), ("LOG10", false, false) },
- { typeof(Math).GetRuntimeMethod(nameof(Math.Max), new[] { typeof(decimal), typeof(decimal) }), ("GREATEST", true, false) },
- { typeof(Math).GetRuntimeMethod(nameof(Math.Max), new[] { typeof(double), typeof(double) }), ("GREATEST", true, false) },
- { typeof(Math).GetRuntimeMethod(nameof(Math.Max), new[] { typeof(float), typeof(float) }), ("GREATEST", true, false) },
- { typeof(Math).GetRuntimeMethod(nameof(Math.Max), new[] { typeof(int), typeof(int) }), ("GREATEST", true, false) },
- { typeof(Math).GetRuntimeMethod(nameof(Math.Max), new[] { typeof(long), typeof(long) }), ("GREATEST", true, false) },
- { typeof(Math).GetRuntimeMethod(nameof(Math.Max), new[] { typeof(short), typeof(short) }), ("GREATEST", true, false) },
- { typeof(MathF).GetRuntimeMethod(nameof(MathF.Max), new[] { typeof(float), typeof(float) }), ("GREATEST", true, false) },
-
- { typeof(Math).GetRuntimeMethod(nameof(Math.Min), new[] { typeof(decimal), typeof(decimal) }), ("LEAST", true, false) },
- { typeof(Math).GetRuntimeMethod(nameof(Math.Min), new[] { typeof(double), typeof(double) }), ("LEAST", true, false) },
- { typeof(Math).GetRuntimeMethod(nameof(Math.Min), new[] { typeof(float), typeof(float) }), ("LEAST", true, false) },
- { typeof(Math).GetRuntimeMethod(nameof(Math.Min), new[] { typeof(int), typeof(int) }), ("LEAST", true, false) },
- { typeof(Math).GetRuntimeMethod(nameof(Math.Min), new[] { typeof(long), typeof(long) }), ("LEAST", true, false) },
- { typeof(Math).GetRuntimeMethod(nameof(Math.Min), new[] { typeof(short), typeof(short) }), ("LEAST", true, false) },
- { typeof(MathF).GetRuntimeMethod(nameof(MathF.Min), new[] { typeof(float), typeof(float) }), ("LEAST", true, false) },
+ // GREATEST is handled in MySqlSqlTranslatingExpressionVisitor.GenerateGreatest():
+ // LEAST is handled in MySqlSqlTranslatingExpressionVisitor.GenerateLeast():
{ typeof(Math).GetRuntimeMethod(nameof(Math.Pow), new[] { typeof(double), typeof(double) }), ("POWER", true, false) },
{ typeof(MathF).GetRuntimeMethod(nameof(MathF.Pow), new[] { typeof(float), typeof(float) }), ("POWER", true, false) },
diff --git a/src/EFCore.MySql/Query/Internal/MySqlObjectToStringTranslator.cs b/src/EFCore.MySql/Query/Internal/MySqlObjectToStringTranslator.cs
index 65cd340f9..473e1d64f 100644
--- a/src/EFCore.MySql/Query/Internal/MySqlObjectToStringTranslator.cs
+++ b/src/EFCore.MySql/Query/Internal/MySqlObjectToStringTranslator.cs
@@ -51,41 +51,49 @@ public virtual SqlExpression Translate(
IReadOnlyList arguments,
IDiagnosticsLogger logger)
{
- if (instance == null ||
- method.Name != nameof(ToString) ||
- arguments.Count != 0)
+ if (instance == null || method.Name != nameof(ToString) || arguments.Count != 0)
{
return null;
}
+ if (instance.TypeMapping?.ClrType == typeof(string))
+ {
+ return instance;
+ }
+
if (instance.Type == typeof(bool))
{
- return instance is ColumnExpression columnExpression &&
- columnExpression.IsNullable
- ? _sqlExpressionFactory.Case(
+ if (instance is not ColumnExpression { IsNullable: false })
+ {
+ return _sqlExpressionFactory.Case(
+ instance,
new[]
{
new CaseWhenClause(
- _sqlExpressionFactory.Equal(instance, _sqlExpressionFactory.Constant(false)),
+ _sqlExpressionFactory.Constant(false),
_sqlExpressionFactory.Constant(false.ToString())),
new CaseWhenClause(
- _sqlExpressionFactory.Equal(instance, _sqlExpressionFactory.Constant(true)),
+ _sqlExpressionFactory.Constant(true),
_sqlExpressionFactory.Constant(true.ToString()))
},
- _sqlExpressionFactory.Constant(null))
- : _sqlExpressionFactory.Case(
- new[]
- {
- new CaseWhenClause(
- _sqlExpressionFactory.Equal(instance, _sqlExpressionFactory.Constant(false)),
- _sqlExpressionFactory.Constant(false.ToString()))
- },
- _sqlExpressionFactory.Constant(true.ToString()));
+ _sqlExpressionFactory.Constant(string.Empty));
+ }
+
+ return _sqlExpressionFactory.Case(
+ new[]
+ {
+ new CaseWhenClause(
+ instance,
+ _sqlExpressionFactory.Constant(true.ToString()))
+ },
+ _sqlExpressionFactory.Constant(false.ToString()));
}
- // Translates parameterless Object.ToString() calls.
- return _supportedTypes.Contains(instance.Type.UnwrapNullableType())
- ? _sqlExpressionFactory.Convert(instance, typeof(string))
+ // Enums are handled by EnumMethodTranslator.
+ return _supportedTypes.Contains(instance.Type)
+ ? _sqlExpressionFactory.Coalesce(
+ _sqlExpressionFactory.Convert(instance, typeof(string)),
+ _sqlExpressionFactory.Constant(string.Empty))
: null;
}
}
diff --git a/src/EFCore.MySql/Query/Internal/MySqlParameterBasedSqlProcessor.cs b/src/EFCore.MySql/Query/Internal/MySqlParameterBasedSqlProcessor.cs
index c93b6ea9f..4111be0d1 100644
--- a/src/EFCore.MySql/Query/Internal/MySqlParameterBasedSqlProcessor.cs
+++ b/src/EFCore.MySql/Query/Internal/MySqlParameterBasedSqlProcessor.cs
@@ -18,9 +18,9 @@ public class MySqlParameterBasedSqlProcessor : RelationalParameterBasedSqlProces
public MySqlParameterBasedSqlProcessor(
RelationalParameterBasedSqlProcessorDependencies dependencies,
- bool useRelationalNulls,
+ RelationalParameterBasedSqlProcessorParameters parameters,
IMySqlOptions options)
- : base(dependencies, useRelationalNulls)
+ : base(dependencies, parameters)
{
_options = options;
}
@@ -46,8 +46,6 @@ public override Expression Optimize(
.Visit(queryExpression);
}
- queryExpression = new MySqlHavingExpressionVisitor((MySqlSqlExpressionFactory)Dependencies.SqlExpressionFactory).Visit(queryExpression);
-
queryExpression = new MySqlParameterInliningExpressionVisitor(
Dependencies.TypeMappingSource,
Dependencies.SqlExpressionFactory,
@@ -70,7 +68,7 @@ protected override Expression ProcessSqlNullability(
Check.NotNull(queryExpression, nameof(queryExpression));
Check.NotNull(parametersValues, nameof(parametersValues));
- queryExpression = new MySqlSqlNullabilityProcessor(Dependencies, UseRelationalNulls)
+ queryExpression = new MySqlSqlNullabilityProcessor(Dependencies, Parameters)
.Process(queryExpression, parametersValues, out canCache);
return queryExpression;
diff --git a/src/EFCore.MySql/Query/Internal/MySqlParameterBasedSqlProcessorFactory.cs b/src/EFCore.MySql/Query/Internal/MySqlParameterBasedSqlProcessorFactory.cs
index 0aeb6915e..4a2fc73db 100644
--- a/src/EFCore.MySql/Query/Internal/MySqlParameterBasedSqlProcessorFactory.cs
+++ b/src/EFCore.MySql/Query/Internal/MySqlParameterBasedSqlProcessorFactory.cs
@@ -20,7 +20,7 @@ public MySqlParameterBasedSqlProcessorFactory(
_options = options;
}
- public virtual RelationalParameterBasedSqlProcessor Create(bool useRelationalNulls)
- => new MySqlParameterBasedSqlProcessor(_dependencies, useRelationalNulls, _options);
+ public virtual RelationalParameterBasedSqlProcessor Create(RelationalParameterBasedSqlProcessorParameters parameters)
+ => new MySqlParameterBasedSqlProcessor(_dependencies, parameters, _options);
}
}
diff --git a/src/EFCore.MySql/Query/Internal/MySqlQueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore.MySql/Query/Internal/MySqlQueryableMethodTranslatingExpressionVisitor.cs
index a16cc6f29..789df5247 100644
--- a/src/EFCore.MySql/Query/Internal/MySqlQueryableMethodTranslatingExpressionVisitor.cs
+++ b/src/EFCore.MySql/Query/Internal/MySqlQueryableMethodTranslatingExpressionVisitor.cs
@@ -2,7 +2,6 @@
// Licensed under the MIT. See LICENSE in the project root for license information.
using System;
-using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Linq.Expressions;
@@ -17,7 +16,6 @@
using Microsoft.EntityFrameworkCore.Utilities;
using Pomelo.EntityFrameworkCore.MySql.Infrastructure.Internal;
using Pomelo.EntityFrameworkCore.MySql.Query.ExpressionTranslators.Internal;
-using Pomelo.EntityFrameworkCore.MySql.Storage.Internal;
namespace Pomelo.EntityFrameworkCore.MySql.Query.Internal;
@@ -361,12 +359,6 @@ elementTypeMapping is not null
return new ShapedQueryExpression(selectExpression, shaperExpression);
}
- protected override Expression ApplyInferredTypeMappings(
- Expression expression,
- IReadOnlyDictionary<(string, string), RelationalTypeMapping> inferredTypeMappings)
- => new MySqlInferredTypeMappingApplier(
- RelationalDependencies.Model, _typeMappingSource, _sqlExpressionFactory, inferredTypeMappings).Visit(expression);
-
///
/// Wraps the given expression with any SQL logic necessary to convert a value coming out of a JSON document into the relational value
/// represented by the given type mapping.
@@ -383,128 +375,6 @@ private static SqlExpression ApplyJsonSqlConversion(
_ => expression
};
- protected class MySqlInferredTypeMappingApplier : RelationalInferredTypeMappingApplier
- {
- private readonly IRelationalTypeMappingSource _typeMappingSource;
- private readonly ISqlExpressionFactory _sqlExpressionFactory;
- private Dictionary _currentSelectInferredTypeMappings;
-
- ///
- /// 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 MySqlInferredTypeMappingApplier(
- IModel model,
- IRelationalTypeMappingSource typeMappingSource,
- ISqlExpressionFactory sqlExpressionFactory,
- IReadOnlyDictionary<(string, string), RelationalTypeMapping> inferredTypeMappings)
- : base(model, sqlExpressionFactory, inferredTypeMappings)
- {
- (_typeMappingSource, _sqlExpressionFactory) = (typeMappingSource, sqlExpressionFactory);
- }
-
- ///
- /// 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 expression)
- {
- switch (expression)
- {
- case MySqlJsonTableExpression { Name: "JSON_TABLE", Schema: null, IsBuiltIn: true } jsonTableExpression
- when TryGetInferredTypeMapping(jsonTableExpression.Alias, "value", out var typeMapping):
- return ApplyTypeMappingsOnJsonTableExpression(jsonTableExpression, typeMapping);
-
- // Above, we applied the type mapping the the parameter that JSON_TABLE accepts as an argument.
- // But the inferred type mapping also needs to be applied as a SQL conversion on the column projections coming out of the
- // SelectExpression containing the JSON_TABLE call. So we set state to know about JSON_TABLE tables and their type mappings
- // in the immediate SelectExpression, and continue visiting down (see ColumnExpression visitation below).
- case SelectExpression selectExpression:
- {
- Dictionary previousSelectInferredTypeMappings = null;
-
- foreach (var table in selectExpression.Tables)
- {
- if (table is TableValuedFunctionExpression { Name: "JSON_TABLE", Schema: null, IsBuiltIn: true } jsonTableExpression
- && TryGetInferredTypeMapping(jsonTableExpression.Alias, "value", out var inferredTypeMapping))
- {
- if (previousSelectInferredTypeMappings is null)
- {
- previousSelectInferredTypeMappings = _currentSelectInferredTypeMappings;
- _currentSelectInferredTypeMappings = new Dictionary();
- }
-
- _currentSelectInferredTypeMappings![jsonTableExpression.Alias] = inferredTypeMapping;
- }
- }
-
- var visited = base.VisitExtension(expression);
-
- _currentSelectInferredTypeMappings = previousSelectInferredTypeMappings;
-
- return visited;
- }
-
- // Note that we match also ColumnExpressions which already have a type mapping, i.e. coming out of column collections (as
- // opposed to parameter collections, where the type mapping needs to be inferred). This is in order to apply SQL conversion
- // logic later in the process, see note in TranslateCollection.
- case ColumnExpression { Name: "value" } columnExpression
- when _currentSelectInferredTypeMappings?.TryGetValue(columnExpression.TableAlias, out var inferredTypeMapping) is true:
- return ApplyJsonSqlConversion(
- columnExpression.ApplyTypeMapping(inferredTypeMapping),
- _sqlExpressionFactory,
- inferredTypeMapping,
- columnExpression.IsNullable);
-
- default:
- return base.VisitExtension(expression);
- }
- }
-
- ///
- /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
- /// the same compatibility standards as public APIs. It may be changed or removed without notice in
- /// any release. You should only use it directly in your code with extreme caution and knowing that
- /// doing so can result in application failures when updating to a new Entity Framework Core release.
- ///
- protected virtual TableValuedFunctionExpression ApplyTypeMappingsOnJsonTableExpression(
- MySqlJsonTableExpression jsonTableExpression,
- RelationalTypeMapping inferredTypeMapping)
- {
- // Constant queryables are translated to VALUES, no need for JSON.
- // Column queryables have their type mapping from the model, so we don't ever need to apply an inferred mapping on them.
- if (jsonTableExpression.JsonExpression is not SqlParameterExpression parameterExpression)
- {
- return jsonTableExpression;
- }
-
- if (_typeMappingSource.FindMapping(parameterExpression.Type, Model, inferredTypeMapping) is not MySqlStringTypeMapping
- parameterTypeMapping)
- {
- throw new InvalidOperationException("Type mapping for 'string' could not be found or was not a MySqlStringTypeMapping");
- }
-
- Check.DebugAssert(parameterTypeMapping.ElementTypeMapping != null, "Collection type mapping missing element mapping.");
-
- return jsonTableExpression.Update(
- parameterExpression.ApplyTypeMapping(parameterTypeMapping),
- jsonTableExpression.Path,
- new[]
- {
- new MySqlJsonTableExpression.ColumnInfo
- {
- Name = "value",
- TypeMapping = (RelationalTypeMapping)parameterTypeMapping.ElementTypeMapping,
- Path = new[] { new PathSegment(_sqlExpressionFactory.Constant(0, _typeMappingSource.FindMapping(typeof(int)))) },
- }
- });
- }
- }
-
private sealed class FakeMemberInfo : MemberInfo
{
public FakeMemberInfo(string name)
diff --git a/src/EFCore.MySql/Query/Internal/MySqlSqlNullabilityProcessor.cs b/src/EFCore.MySql/Query/Internal/MySqlSqlNullabilityProcessor.cs
index 40f7daf62..ba7c868ca 100644
--- a/src/EFCore.MySql/Query/Internal/MySqlSqlNullabilityProcessor.cs
+++ b/src/EFCore.MySql/Query/Internal/MySqlSqlNullabilityProcessor.cs
@@ -20,11 +20,11 @@ public class MySqlSqlNullabilityProcessor : SqlNullabilityProcessor
/// Creates a new instance of the class.
///
/// Parameter object containing dependencies for this class.
- /// A bool value indicating whether relational null semantics are in use.
+ /// Parameter object containing parameters for this class.
public MySqlSqlNullabilityProcessor(
[NotNull] RelationalParameterBasedSqlProcessorDependencies dependencies,
- bool useRelationalNulls)
- : base(dependencies, useRelationalNulls)
+ RelationalParameterBasedSqlProcessorParameters parameters)
+ : base(dependencies, parameters)
=> _sqlExpressionFactory = dependencies.SqlExpressionFactory;
///
diff --git a/src/EFCore.MySql/Query/Internal/MySqlStringMemberTranslator.cs b/src/EFCore.MySql/Query/Internal/MySqlStringMemberTranslator.cs
index 4e6031b4e..97fa57f75 100644
--- a/src/EFCore.MySql/Query/Internal/MySqlStringMemberTranslator.cs
+++ b/src/EFCore.MySql/Query/Internal/MySqlStringMemberTranslator.cs
@@ -26,7 +26,7 @@ public virtual SqlExpression Translate(
IDiagnosticsLogger logger)
{
if (member.Name == nameof(string.Length)
- && instance?.Type == typeof(string))
+ && member.DeclaringType == typeof(string))
{
return _sqlExpressionFactory.NullableFunction(
"CHAR_LENGTH",
diff --git a/src/EFCore.MySql/Query/Internal/MySqlStringMethodTranslator.cs b/src/EFCore.MySql/Query/Internal/MySqlStringMethodTranslator.cs
index acadb066d..827447326 100644
--- a/src/EFCore.MySql/Query/Internal/MySqlStringMethodTranslator.cs
+++ b/src/EFCore.MySql/Query/Internal/MySqlStringMethodTranslator.cs
@@ -110,6 +110,17 @@ private static readonly MethodInfo _removeMethodInfoWithTwoArgs
p.ParameterType.GetGenericTypeDefinition() == typeof(IEnumerable<>))))
.ToArray();
+ private static readonly MethodInfo[] _joinMethodInfos = typeof(string).GetRuntimeMethods()
+ .Where(
+ m => m is { Name: nameof(string.Join) } &&
+ m.GetParameters() is { Length: 2 } parameters &&
+ (parameters[0].ParameterType == typeof(string) ||
+ parameters[0].ParameterType == typeof(char)) &&
+ (parameters[1].ParameterType == typeof(string[]) ||
+ parameters[1].ParameterType == typeof(object[]) ||
+ parameters[1].ParameterType == typeof(IEnumerable<>)))
+ .ToArray();
+
private readonly MySqlSqlExpressionFactory _sqlExpressionFactory;
public MySqlStringMethodTranslator(
@@ -169,7 +180,7 @@ public override SqlExpression Translate(
_sqlExpressionFactory.IsNotNull(replacementArgument),
replaceCall)
},
- _sqlExpressionFactory.Constant(null, RelationalTypeMapping.NullMapping));
+ _sqlExpressionFactory.Constant(null, replaceCall.Type, replaceCall.TypeMapping));
}
if (_toLowerMethodInfo.Equals(method)
@@ -431,6 +442,54 @@ public override SqlExpression Translate(
onlyNullWhenAnyNullPropagatingArgumentIsNull: arguments[0] is not MySqlComplexFunctionArgumentExpression);
}
+ if (_joinMethodInfos.Contains(
+ (method.IsGenericMethod
+ ? method.GetGenericMethodDefinition()
+ : null) ?? method))
+ {
+ // Handle
+ // char, object[]
+ // char, string[]
+ // char, IEnumerable
+ // string, object[]
+ // string, string[]
+ // string, IEnumerable
+ // string, IEnumerable
+ //
+ // Some call signature variants can never reach this code, because they will be directly called and thus only their result
+ // is translated.
+ var concatWsArguments = arguments[1] is MySqlComplexFunctionArgumentExpression mySqlComplexFunctionArgumentExpression
+ ? [
+ arguments[0],
+ // CONCAT_WS filters out nulls, but string.Join treats them as empty strings; so coalesce (which is a no-op for
+ // non-nullable arguments).
+ mySqlComplexFunctionArgumentExpression.Update(
+ mySqlComplexFunctionArgumentExpression.ArgumentParts
+ .Select(e => _sqlExpressionFactory.Coalesce(e, _sqlExpressionFactory.Constant(string.Empty)))
+ .ToList(),
+ mySqlComplexFunctionArgumentExpression.Delimiter)]
+ : arguments.Select(
+ e => e switch
+ {
+ SqlConstantExpression c => _sqlExpressionFactory.Constant(c.Value.ToString()),
+ SqlParameterExpression p => p.ApplyTypeMapping(
+ ((MySqlStringTypeMapping)_typeMappingSource.GetMapping(typeof(string))).Clone(forceToString: true)),
+ _ => e,
+ })
+ .Prepend(arguments[0])
+ .ToArray();
+
+ // We haven't implemented expansion of MySqlComplexFunctionArgumentExpression yet, so the default nullability check would
+ // result in an invalid SQL generation.
+ // TODO: Fix at some point.
+ return _sqlExpressionFactory.NullableFunction(
+ "CONCAT_WS",
+ concatWsArguments,
+ method.ReturnType,
+ onlyNullWhenAnyNullPropagatingArgumentIsNull: arguments[0] is not MySqlComplexFunctionArgumentExpression,
+ argumentsPropagateNullability: [true, false]);
+ }
+
return null;
}
diff --git a/src/EFCore.MySql/Query/Internal/SkipTakeCollapsingExpressionVisitor.cs b/src/EFCore.MySql/Query/Internal/SkipTakeCollapsingExpressionVisitor.cs
index 217c83d53..4ec971bb3 100644
--- a/src/EFCore.MySql/Query/Internal/SkipTakeCollapsingExpressionVisitor.cs
+++ b/src/EFCore.MySql/Query/Internal/SkipTakeCollapsingExpressionVisitor.cs
@@ -52,7 +52,6 @@ protected override Expression VisitExtension(Expression extensionExpression)
&& IsZero(selectExpression.Offset))
{
return selectExpression.Update(
- selectExpression.Projection,
selectExpression.Tables,
selectExpression.GroupBy.Count > 0
? selectExpression.Predicate
@@ -61,9 +60,10 @@ protected override Expression VisitExtension(Expression extensionExpression)
selectExpression.GroupBy.Count > 0
? _sqlExpressionFactory.ApplyDefaultTypeMapping(_sqlExpressionFactory.Constant(false))
: null,
+ selectExpression.Projection,
new List(0),
- limit: null,
- offset: null);
+ offset: null,
+ limit: null);
}
bool IsZero(SqlExpression? sqlExpression)
diff --git a/src/EFCore.MySql/Scaffolding/Internal/MySqlDatabaseModelFactory.cs b/src/EFCore.MySql/Scaffolding/Internal/MySqlDatabaseModelFactory.cs
index dbcfe7883..ca8807180 100644
--- a/src/EFCore.MySql/Scaffolding/Internal/MySqlDatabaseModelFactory.cs
+++ b/src/EFCore.MySql/Scaffolding/Internal/MySqlDatabaseModelFactory.cs
@@ -208,7 +208,7 @@ private static Func GenerateSchemaFilter(IReadOnlyList s
FROM
`INFORMATION_SCHEMA`.`TABLES` as `t`
LEFT JOIN
- `INFORMATION_SCHEMA`.`COLLATION_CHARACTER_SET_APPLICABILITY` as `ccsa` ON `ccsa`.`COLLATION_NAME` = `t`.`TABLE_COLLATION`
+ `INFORMATION_SCHEMA`.`COLLATION_CHARACTER_SET_APPLICABILITY` as `ccsa` ON `ccsa`.`{0}` = `t`.`TABLE_COLLATION`
WHERE
`TABLE_SCHEMA` = SCHEMA()
AND
@@ -222,8 +222,12 @@ protected virtual IEnumerable GetTables(
{
using (var command = connection.CreateCommand())
{
+ var collationColumnName = _options.ServerVersion.Supports.CollationCharacterSetApplicabilityWithFullCollationNameColumn
+ ? "FULL_COLLATION_NAME"
+ : "COLLATION_NAME";
+
var tables = new List();
- command.CommandText = GetTablesQuery;
+ command.CommandText = string.Format(GetTablesQuery, collationColumnName);
using (var reader = command.ExecuteReader())
{
while (reader.Read())
diff --git a/src/EFCore.MySql/Storage/Internal/Json/MySqlJsonByteArrayAsHexStringReaderWriter.cs b/src/EFCore.MySql/Storage/Internal/Json/MySqlJsonByteArrayAsHexStringReaderWriter.cs
index 48631c6c6..eaa7a2238 100644
--- a/src/EFCore.MySql/Storage/Internal/Json/MySqlJsonByteArrayAsHexStringReaderWriter.cs
+++ b/src/EFCore.MySql/Storage/Internal/Json/MySqlJsonByteArrayAsHexStringReaderWriter.cs
@@ -2,6 +2,8 @@
// Licensed under the MIT. See LICENSE in the project root for license information.
using System;
+using System.Linq.Expressions;
+using System.Reflection;
using System.Text.Json;
using Microsoft.EntityFrameworkCore.Storage.Json;
@@ -9,6 +11,9 @@ namespace Pomelo.EntityFrameworkCore.MySql.Storage.Internal.Json;
public sealed class MySqlJsonByteArrayAsHexStringReaderWriter : JsonValueReaderWriter
{
+ public static readonly PropertyInfo InstanceProperty =
+ typeof(MySqlJsonByteArrayAsHexStringReaderWriter).GetProperty(nameof(Instance));
+
public static MySqlJsonByteArrayAsHexStringReaderWriter Instance { get; } = new();
private MySqlJsonByteArrayAsHexStringReaderWriter()
@@ -20,4 +25,7 @@ public override byte[] FromJsonTyped(ref Utf8JsonReaderManager manager, object e
public override void ToJsonTyped(Utf8JsonWriter writer, byte[] value)
=> writer.WriteStringValue(Convert.ToHexString(value));
+
+ public override Expression ConstructorExpression
+ => Expression.Property(null, InstanceProperty);
}
diff --git a/test/EFCore.MySql.FunctionalTests/BuiltInDataTypesMySqlTest.cs b/test/EFCore.MySql.FunctionalTests/BuiltInDataTypesMySqlTest.cs
index d8f50e5e9..a7d006a58 100644
--- a/test/EFCore.MySql.FunctionalTests/BuiltInDataTypesMySqlTest.cs
+++ b/test/EFCore.MySql.FunctionalTests/BuiltInDataTypesMySqlTest.cs
@@ -5,6 +5,7 @@
using System.Linq;
using System.Linq.Expressions;
using System.Text;
+using System.Threading.Tasks;
using Pomelo.EntityFrameworkCore.MySql.FunctionalTests.TestUtilities;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
@@ -32,12 +33,12 @@ public BuiltInDataTypesMySqlTest(BuiltInDataTypesMySqlFixture fixture, ITestOutp
//Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper);
}
- public override void Object_to_string_conversion()
+ public override async Task Object_to_string_conversion()
{
using var context = CreateContext();
- var expected = context.Set()
- .Where(e => e.Id == 13)
- .AsEnumerable()
+ var expected = (await context.Set()
+ .Where(e => e.Id == 13)
+ .ToListAsync())
.Select(
b => new
{
@@ -56,7 +57,7 @@ public override void Object_to_string_conversion()
Fixture.ListLoggerFactory.Clear();
- var query = context.Set()
+ var query = await context.Set()
.Where(e => e.Id == 13)
.Select(
b => new
@@ -77,7 +78,7 @@ public override void Object_to_string_conversion()
DateTimeOffset = b.TestDateTimeOffset.ToString(),
TimeSpan = b.TestTimeSpan.ToString()
})
- .ToList();
+ .ToListAsync();
var actual = Assert.Single(query);
Assert.Equal(expected.Sbyte, actual.Sbyte);
@@ -875,7 +876,7 @@ public virtual void Can_insert_and_read_back_all_mapped_data_types_set_to_null()
// Overridden because of TestNullableDateTimeOffset, since MySQL does not offer a native data type to save a date/time with
// timezone.
- public override void Can_insert_and_read_back_all_nullable_data_types_with_values_set_to_non_null()
+ public override async Task Can_insert_and_read_back_all_nullable_data_types_with_values_set_to_non_null()
{
using (var context = CreateContext())
{
@@ -912,12 +913,12 @@ public override void Can_insert_and_read_back_all_nullable_data_types_with_value
EnumS8 = EnumS8.SomeValue
});
- Assert.Equal(1, context.SaveChanges());
+ Assert.Equal(1, await context.SaveChangesAsync());
}
using (var context = CreateContext())
{
- var dt = context.Set().Where(ndt => ndt.Id == 101).ToList().Single();
+ var dt = (await context.Set().Where(ndt => ndt.Id == 101).ToListAsync()).Single();
var entityType = context.Model.FindEntityType(typeof(BuiltInNullableDataTypes));
AssertEqualIfMapped(entityType, "TestString", () => dt.TestString);
@@ -1521,7 +1522,7 @@ public void Can_get_column_types_from_built_model()
#region https://github.com/dotnet/efcore/issues/26068
[ConditionalFact]
- public override void Can_insert_and_read_back_all_non_nullable_data_types()
+ public override async Task Can_insert_and_read_back_all_non_nullable_data_types()
{
using (var context = CreateContext())
{
@@ -1556,12 +1557,12 @@ public override void Can_insert_and_read_back_all_non_nullable_data_types()
EnumS8 = EnumS8.SomeValue
});
- Assert.Equal(1, context.SaveChanges());
+ Assert.Equal(1, await context.SaveChangesAsync());
}
using (var context = CreateContext())
{
- var dt = context.Set().Where(e => e.Id == 1).ToList().Single();
+ var dt = (await context.Set().Where(e => e.Id == 1).ToListAsync()).Single();
var entityType = context.Model.FindEntityType(typeof(BuiltInDataTypes));
AssertEqualIfMapped(entityType, (short)-1234, () => dt.TestInt16);
@@ -1595,7 +1596,7 @@ public override void Can_insert_and_read_back_all_non_nullable_data_types()
}
[ConditionalFact]
- public override void Can_insert_and_read_back_non_nullable_backed_data_types()
+ public override async Task Can_insert_and_read_back_non_nullable_backed_data_types()
{
using (var context = CreateContext())
{
@@ -1630,12 +1631,12 @@ public override void Can_insert_and_read_back_non_nullable_backed_data_types()
EnumS8 = EnumS8.SomeValue
});
- Assert.Equal(1, context.SaveChanges());
+ Assert.Equal(1, await context.SaveChangesAsync());
}
using (var context = CreateContext())
{
- var dt = context.Set().Where(ndt => ndt.Id == 101).ToList().Single();
+ var dt = (await context.Set().Where(ndt => ndt.Id == 101).ToListAsync()).Single();
var entityType = context.Model.FindEntityType(typeof(NonNullableBackedDataTypes));
AssertEqualIfMapped(entityType, (short)-1234, () => dt.Int16);
@@ -1669,7 +1670,7 @@ public override void Can_insert_and_read_back_non_nullable_backed_data_types()
}
[ConditionalFact]
- public override void Can_insert_and_read_back_nullable_backed_data_types()
+ public override async Task Can_insert_and_read_back_nullable_backed_data_types()
{
using (var context = CreateContext())
{
@@ -1704,12 +1705,12 @@ public override void Can_insert_and_read_back_nullable_backed_data_types()
EnumS8 = EnumS8.SomeValue
});
- Assert.Equal(1, context.SaveChanges());
+ Assert.Equal(1, await context.SaveChangesAsync());
}
using (var context = CreateContext())
{
- var dt = context.Set().Where(ndt => ndt.Id == 101).ToList().Single();
+ var dt = (await context.Set().Where(ndt => ndt.Id == 101).ToListAsync()).Single();
var entityType = context.Model.FindEntityType(typeof(NullableBackedDataTypes));
AssertEqualIfMapped(entityType, (short)-1234, () => dt.Int16);
@@ -1742,7 +1743,7 @@ public override void Can_insert_and_read_back_nullable_backed_data_types()
}
[ConditionalFact]
- public override void Can_insert_and_read_back_object_backed_data_types()
+ public override async Task Can_insert_and_read_back_object_backed_data_types()
{
using (var context = CreateContext())
{
@@ -1779,12 +1780,12 @@ public override void Can_insert_and_read_back_object_backed_data_types()
EnumS8 = EnumS8.SomeValue
});
- Assert.Equal(1, context.SaveChanges());
+ Assert.Equal(1, await context.SaveChangesAsync());
}
using (var context = CreateContext())
{
- var dt = context.Set().Where(ndt => ndt.Id == 101).ToList().Single();
+ var dt = (await context.Set().Where(ndt => ndt.Id == 101).ToListAsync()).Single();
var entityType = context.Model.FindEntityType(typeof(ObjectBackedDataTypes));
AssertEqualIfMapped(entityType, "TestString", () => dt.String);
diff --git a/test/EFCore.MySql.FunctionalTests/BulkUpdates/ComplexTypeBulkUpdatesMySqlTest.cs b/test/EFCore.MySql.FunctionalTests/BulkUpdates/ComplexTypeBulkUpdatesMySqlTest.cs
index f87694622..b399cb473 100644
--- a/test/EFCore.MySql.FunctionalTests/BulkUpdates/ComplexTypeBulkUpdatesMySqlTest.cs
+++ b/test/EFCore.MySql.FunctionalTests/BulkUpdates/ComplexTypeBulkUpdatesMySqlTest.cs
@@ -7,7 +7,7 @@
namespace Pomelo.EntityFrameworkCore.MySql.FunctionalTests.BulkUpdates;
-public class ComplexTypeBulkUpdatesMySqlTest : ComplexTypeBulkUpdatesTestBase<
+public class ComplexTypeBulkUpdatesMySqlTest : ComplexTypeBulkUpdatesRelationalTestBase<
ComplexTypeBulkUpdatesMySqlTest.ComplexTypeBulkUpdatesMySqlFixture>
{
public ComplexTypeBulkUpdatesMySqlTest(ComplexTypeBulkUpdatesMySqlFixture fixture, ITestOutputHelper testOutputHelper)
@@ -27,9 +27,9 @@ public override async Task Delete_entity_type_with_complex_type(bool async)
""");
}
- public override async Task Delete_complex_type_throws(bool async)
+ public override async Task Delete_complex_type(bool async)
{
- await base.Delete_complex_type_throws(async);
+ await base.Delete_complex_type(async);
AssertSql();
}
@@ -95,9 +95,9 @@ public override async Task Update_multiple_projected_complex_types_via_anonymous
""");
}
- public override async Task Update_projected_complex_type_via_OrderBy_Skip_throws(bool async)
+ public override async Task Update_projected_complex_type_via_OrderBy_Skip(bool async)
{
- await base.Update_projected_complex_type_via_OrderBy_Skip_throws(async);
+ await base.Update_projected_complex_type_via_OrderBy_Skip(async);
AssertExecuteUpdateSql();
}
@@ -304,7 +304,7 @@ public override async Task Update_collection_inside_complex_type(bool async)
[ConditionalFact]
public virtual void Check_all_tests_overridden()
{
- TestHelpers.AssertAllMethodsOverridden(GetType());
+ MySqlTestHelpers.AssertAllMethodsOverridden(GetType());
}
private void AssertExecuteUpdateSql(params string[] expected)
@@ -322,7 +322,7 @@ protected void ClearLog()
Fixture.TestSqlLoggerFactory.Clear();
}
- public class ComplexTypeBulkUpdatesMySqlFixture : ComplexTypeBulkUpdatesFixtureBase
+ public class ComplexTypeBulkUpdatesMySqlFixture : ComplexTypeBulkUpdatesRelationalFixtureBase
{
protected override ITestStoreFactory TestStoreFactory
=> MySqlTestStoreFactory.Instance;
diff --git a/test/EFCore.MySql.FunctionalTests/BulkUpdates/NonSharedModelBulkUpdatesMySqlTest.cs b/test/EFCore.MySql.FunctionalTests/BulkUpdates/NonSharedModelBulkUpdatesMySqlTest.cs
index 81c2441cd..d11cd5bf4 100644
--- a/test/EFCore.MySql.FunctionalTests/BulkUpdates/NonSharedModelBulkUpdatesMySqlTest.cs
+++ b/test/EFCore.MySql.FunctionalTests/BulkUpdates/NonSharedModelBulkUpdatesMySqlTest.cs
@@ -7,14 +7,14 @@
namespace Pomelo.EntityFrameworkCore.MySql.FunctionalTests.BulkUpdates;
-public class NonSharedModelBulkUpdatesMySqlTest : NonSharedModelBulkUpdatesTestBase
+public class NonSharedModelBulkUpdatesMySqlTest : NonSharedModelBulkUpdatesRelationalTestBase
{
protected override ITestStoreFactory TestStoreFactory
=> MySqlTestStoreFactory.Instance;
[ConditionalFact]
public virtual void Check_all_tests_overridden()
- => TestHelpers.AssertAllMethodsOverridden(GetType());
+ => MySqlTestHelpers.AssertAllMethodsOverridden(GetType());
public override async Task Delete_aggregate_root_when_eager_loaded_owned_collection(bool async)
{
@@ -88,7 +88,7 @@ public override async Task Update_owned_and_non_owned_properties_with_table_shar
"""
UPDATE `Owner` AS `o`
SET `o`.`OwnedReference_Number` = CHAR_LENGTH(`o`.`Title`),
- `o`.`Title` = CAST(`o`.`OwnedReference_Number` AS char)
+ `o`.`Title` = COALESCE(CAST(`o`.`OwnedReference_Number` AS char), '')
""");
}
@@ -158,6 +158,18 @@ public override async Task Update_non_owned_property_on_entity_with_owned_in_joi
""");
}
+ public override async Task Replace_ColumnExpression_in_column_setter(bool async)
+ {
+ await base.Replace_ColumnExpression_in_column_setter(async);
+
+ AssertSql(
+"""
+UPDATE `Owner` AS `o`
+INNER JOIN `OwnedCollection` AS `o0` ON `o`.`Id` = `o0`.`OwnerId`
+SET `o0`.`Value` = 'SomeValue'
+""");
+ }
+
private void AssertSql(params string[] expected)
=> TestSqlLoggerFactory.AssertBaseline(expected);
diff --git a/test/EFCore.MySql.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesMySqlFixture.cs b/test/EFCore.MySql.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesMySqlFixture.cs
index 5b78b4580..623ef770a 100644
--- a/test/EFCore.MySql.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesMySqlFixture.cs
+++ b/test/EFCore.MySql.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesMySqlFixture.cs
@@ -7,8 +7,8 @@
namespace Pomelo.EntityFrameworkCore.MySql.FunctionalTests.BulkUpdates;
-public class NorthwindBulkUpdatesMySqlFixture : NorthwindBulkUpdatesFixture
- where TModelCustomizer : IModelCustomizer, new()
+public class NorthwindBulkUpdatesMySqlFixture : NorthwindBulkUpdatesRelationalFixture
+ where TModelCustomizer : ITestModelCustomizer, new()
{
protected override ITestStoreFactory TestStoreFactory
=> MySqlNorthwindTestStoreFactory.Instance;
diff --git a/test/EFCore.MySql.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesMySqlTest.cs b/test/EFCore.MySql.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesMySqlTest.cs
index d00896714..6dbd2725f 100644
--- a/test/EFCore.MySql.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesMySqlTest.cs
+++ b/test/EFCore.MySql.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesMySqlTest.cs
@@ -4,6 +4,7 @@
using Microsoft.EntityFrameworkCore.TestModels.Northwind;
using Microsoft.EntityFrameworkCore.TestUtilities;
using MySqlConnector;
+using Pomelo.EntityFrameworkCore.MySql.FunctionalTests.TestUtilities;
using Pomelo.EntityFrameworkCore.MySql.Infrastructure;
using Pomelo.EntityFrameworkCore.MySql.Tests;
using Pomelo.EntityFrameworkCore.MySql.Tests.TestUtilities.Attributes;
@@ -12,7 +13,7 @@
namespace Pomelo.EntityFrameworkCore.MySql.FunctionalTests.BulkUpdates;
-public class NorthwindBulkUpdatesMySqlTest : NorthwindBulkUpdatesTestBase>
+public class NorthwindBulkUpdatesMySqlTest : NorthwindBulkUpdatesRelationalTestBase>
{
public NorthwindBulkUpdatesMySqlTest(
NorthwindBulkUpdatesMySqlFixture fixture,
@@ -25,7 +26,7 @@ public NorthwindBulkUpdatesMySqlTest(
[ConditionalFact]
public virtual void Check_all_tests_overridden()
- => TestHelpers.AssertAllMethodsOverridden(GetType());
+ => MySqlTestHelpers.AssertAllMethodsOverridden(GetType());
public override async Task Delete_Where_TagWith(bool async)
{
@@ -1387,12 +1388,12 @@ public override async Task Update_Where_Join_set_property_from_joined_single_res
AssertExecuteUpdateSql(
"""
UPDATE `Customers` AS `c`
-SET `c`.`City` = CAST(EXTRACT(year FROM (
+SET `c`.`City` = COALESCE(CAST(EXTRACT(year FROM (
SELECT `o`.`OrderDate`
FROM `Orders` AS `o`
WHERE `c`.`CustomerID` = `o`.`CustomerID`
ORDER BY `o`.`OrderDate` DESC
- LIMIT 1)) AS char)
+ LIMIT 1)) AS char), '')
WHERE `c`.`CustomerID` LIKE 'F%'
""");
}
@@ -1421,12 +1422,12 @@ public override async Task Update_Where_Join_set_property_from_joined_single_res
AssertExecuteUpdateSql(
"""
UPDATE `Customers` AS `c`
-SET `c`.`City` = CAST(EXTRACT(year FROM (
+SET `c`.`City` = COALESCE(CAST(EXTRACT(year FROM (
SELECT `o`.`OrderDate`
FROM `Orders` AS `o`
WHERE `c`.`CustomerID` = `o`.`CustomerID`
ORDER BY `o`.`OrderDate` DESC
- LIMIT 1)) AS char)
+ LIMIT 1)) AS char), '')
WHERE `c`.`CustomerID` LIKE 'F%'
""");
}
diff --git a/test/EFCore.MySql.FunctionalTests/BulkUpdates/TPCFiltersInheritanceBulkUpdatesMySqlTest.cs b/test/EFCore.MySql.FunctionalTests/BulkUpdates/TPCFiltersInheritanceBulkUpdatesMySqlTest.cs
index 2a7aab5d6..656a32ce5 100644
--- a/test/EFCore.MySql.FunctionalTests/BulkUpdates/TPCFiltersInheritanceBulkUpdatesMySqlTest.cs
+++ b/test/EFCore.MySql.FunctionalTests/BulkUpdates/TPCFiltersInheritanceBulkUpdatesMySqlTest.cs
@@ -1,6 +1,7 @@
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore.BulkUpdates;
using Microsoft.EntityFrameworkCore.TestUtilities;
+using Pomelo.EntityFrameworkCore.MySql.FunctionalTests.TestUtilities;
using Xunit;
using Xunit.Abstractions;
@@ -19,7 +20,7 @@ public TPCFiltersInheritanceBulkUpdatesMySqlTest(
[ConditionalFact]
public virtual void Check_all_tests_overridden()
- => TestHelpers.AssertAllMethodsOverridden(GetType());
+ => MySqlTestHelpers.AssertAllMethodsOverridden(GetType());
public override async Task Delete_where_hierarchy(bool async)
{
diff --git a/test/EFCore.MySql.FunctionalTests/BulkUpdates/TPCInheritanceBulkUpdatesMySqlTest.cs b/test/EFCore.MySql.FunctionalTests/BulkUpdates/TPCInheritanceBulkUpdatesMySqlTest.cs
index 40d0bf351..8ea90d35c 100644
--- a/test/EFCore.MySql.FunctionalTests/BulkUpdates/TPCInheritanceBulkUpdatesMySqlTest.cs
+++ b/test/EFCore.MySql.FunctionalTests/BulkUpdates/TPCInheritanceBulkUpdatesMySqlTest.cs
@@ -1,6 +1,7 @@
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore.BulkUpdates;
using Microsoft.EntityFrameworkCore.TestUtilities;
+using Pomelo.EntityFrameworkCore.MySql.FunctionalTests.TestUtilities;
using Xunit;
using Xunit.Abstractions;
@@ -18,7 +19,7 @@ public TPCInheritanceBulkUpdatesMySqlTest(
[ConditionalFact]
public virtual void Check_all_tests_overridden()
- => TestHelpers.AssertAllMethodsOverridden(GetType());
+ => MySqlTestHelpers.AssertAllMethodsOverridden(GetType());
public override async Task Delete_where_hierarchy(bool async)
{
diff --git a/test/EFCore.MySql.FunctionalTests/BulkUpdates/TPHFiltersInheritanceBulkUpdatesMySqlTest.cs b/test/EFCore.MySql.FunctionalTests/BulkUpdates/TPHFiltersInheritanceBulkUpdatesMySqlTest.cs
index 5865e1469..9fb819758 100644
--- a/test/EFCore.MySql.FunctionalTests/BulkUpdates/TPHFiltersInheritanceBulkUpdatesMySqlTest.cs
+++ b/test/EFCore.MySql.FunctionalTests/BulkUpdates/TPHFiltersInheritanceBulkUpdatesMySqlTest.cs
@@ -1,9 +1,9 @@
using System;
using System.Threading.Tasks;
-using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.BulkUpdates;
using Microsoft.EntityFrameworkCore.TestUtilities;
using MySqlConnector;
+using Pomelo.EntityFrameworkCore.MySql.FunctionalTests.TestUtilities;
using Pomelo.EntityFrameworkCore.MySql.Infrastructure;
using Pomelo.EntityFrameworkCore.MySql.Tests;
using Pomelo.EntityFrameworkCore.MySql.Tests.TestUtilities.Attributes;
@@ -22,7 +22,7 @@ public TPHFiltersInheritanceBulkUpdatesMySqlTest(TPHFiltersInheritanceBulkUpdate
[ConditionalFact]
public virtual void Check_all_tests_overridden()
- => TestHelpers.AssertAllMethodsOverridden(GetType());
+ => MySqlTestHelpers.AssertAllMethodsOverridden(GetType());
public override async Task Delete_where_hierarchy(bool async)
{
@@ -162,13 +162,6 @@ SELECT COUNT(*)
""");
}
- public override async Task Update_where_keyless_entity_mapped_to_sql_query(bool async)
- {
- await base.Update_where_keyless_entity_mapped_to_sql_query(async);
-
- AssertExecuteUpdateSql();
- }
-
public override async Task Update_base_type(bool async)
{
await base.Update_base_type(async);
@@ -238,19 +231,12 @@ public override async Task Delete_where_hierarchy_subquery(bool async)
AssertSql();
}
- public override async Task Delete_where_keyless_entity_mapped_to_sql_query(bool async)
- {
- await base.Delete_where_keyless_entity_mapped_to_sql_query(async);
-
- AssertSql();
- }
-
- protected override void ClearLog()
- => Fixture.TestSqlLoggerFactory.Clear();
-
private void AssertSql(params string[] expected)
=> Fixture.TestSqlLoggerFactory.AssertBaseline(expected);
private void AssertExecuteUpdateSql(params string[] expected)
=> Fixture.TestSqlLoggerFactory.AssertBaseline(expected, forUpdate: true);
+
+ private void ClearLog()
+ => Fixture.TestSqlLoggerFactory.Clear();
}
diff --git a/test/EFCore.MySql.FunctionalTests/BulkUpdates/TPHInheritanceBulkUpdatesMySqlTest.cs b/test/EFCore.MySql.FunctionalTests/BulkUpdates/TPHInheritanceBulkUpdatesMySqlTest.cs
index 133b2bab8..47723adc7 100644
--- a/test/EFCore.MySql.FunctionalTests/BulkUpdates/TPHInheritanceBulkUpdatesMySqlTest.cs
+++ b/test/EFCore.MySql.FunctionalTests/BulkUpdates/TPHInheritanceBulkUpdatesMySqlTest.cs
@@ -2,23 +2,27 @@
using Microsoft.EntityFrameworkCore.BulkUpdates;
using Microsoft.EntityFrameworkCore.TestUtilities;
using MySqlConnector;
+using Pomelo.EntityFrameworkCore.MySql.FunctionalTests.TestUtilities;
using Pomelo.EntityFrameworkCore.MySql.Infrastructure;
using Pomelo.EntityFrameworkCore.MySql.Tests.TestUtilities.Attributes;
using Xunit;
+using Xunit.Abstractions;
namespace Pomelo.EntityFrameworkCore.MySql.FunctionalTests.BulkUpdates;
public class TPHInheritanceBulkUpdatesMySqlTest : TPHInheritanceBulkUpdatesTestBase
{
- public TPHInheritanceBulkUpdatesMySqlTest(TPHInheritanceBulkUpdatesMySqlFixture fixture)
- : base(fixture)
+ public TPHInheritanceBulkUpdatesMySqlTest(
+ TPHInheritanceBulkUpdatesMySqlFixture fixture,
+ ITestOutputHelper testOutputHelper)
+ : base(fixture, testOutputHelper)
{
ClearLog();
}
[ConditionalFact]
public virtual void Check_all_tests_overridden()
- => TestHelpers.AssertAllMethodsOverridden(GetType());
+ => MySqlTestHelpers.AssertAllMethodsOverridden(GetType());
public override async Task Delete_where_hierarchy(bool async)
{
diff --git a/test/EFCore.MySql.FunctionalTests/BulkUpdates/TPTFiltersInheritanceBulkUpdatesMySqlTest.cs b/test/EFCore.MySql.FunctionalTests/BulkUpdates/TPTFiltersInheritanceBulkUpdatesMySqlTest.cs
index 5c3f4220f..519dfcd30 100644
--- a/test/EFCore.MySql.FunctionalTests/BulkUpdates/TPTFiltersInheritanceBulkUpdatesMySqlTest.cs
+++ b/test/EFCore.MySql.FunctionalTests/BulkUpdates/TPTFiltersInheritanceBulkUpdatesMySqlTest.cs
@@ -1,6 +1,7 @@
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore.BulkUpdates;
using Microsoft.EntityFrameworkCore.TestUtilities;
+using Pomelo.EntityFrameworkCore.MySql.FunctionalTests.TestUtilities;
using Xunit;
using Xunit.Abstractions;
@@ -19,7 +20,7 @@ public TPTFiltersInheritanceBulkUpdatesMySqlTest(
[ConditionalFact]
public virtual void Check_all_tests_overridden()
- => TestHelpers.AssertAllMethodsOverridden(GetType());
+ => MySqlTestHelpers.AssertAllMethodsOverridden(GetType());
public override async Task Delete_where_hierarchy(bool async)
{
diff --git a/test/EFCore.MySql.FunctionalTests/BulkUpdates/TPTInheritanceBulkUpdatesMySqlTest.cs b/test/EFCore.MySql.FunctionalTests/BulkUpdates/TPTInheritanceBulkUpdatesMySqlTest.cs
index 37e1f31c1..77bcc946d 100644
--- a/test/EFCore.MySql.FunctionalTests/BulkUpdates/TPTInheritanceBulkUpdatesMySqlTest.cs
+++ b/test/EFCore.MySql.FunctionalTests/BulkUpdates/TPTInheritanceBulkUpdatesMySqlTest.cs
@@ -1,6 +1,7 @@
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore.BulkUpdates;
using Microsoft.EntityFrameworkCore.TestUtilities;
+using Pomelo.EntityFrameworkCore.MySql.FunctionalTests.TestUtilities;
using Xunit;
using Xunit.Abstractions;
@@ -18,7 +19,7 @@ public TPTInheritanceBulkUpdatesMySqlTest(
[ConditionalFact]
public virtual void Check_all_tests_overridden()
- => TestHelpers.AssertAllMethodsOverridden(GetType());
+ => MySqlTestHelpers.AssertAllMethodsOverridden(GetType());
public override async Task Delete_where_hierarchy(bool async)
{
diff --git a/test/EFCore.MySql.FunctionalTests/ConnectionMySqlTest.cs b/test/EFCore.MySql.FunctionalTests/ConnectionMySqlTest.cs
index 0b2238ba1..38badf3b3 100644
--- a/test/EFCore.MySql.FunctionalTests/ConnectionMySqlTest.cs
+++ b/test/EFCore.MySql.FunctionalTests/ConnectionMySqlTest.cs
@@ -1,4 +1,6 @@
using System;
+using System.Threading;
+using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage;
@@ -53,65 +55,65 @@ public virtual void SetConnectionString_affects_master_connection()
}
[Fact]
- public void Can_create_admin_connection_with_data_source()
+ public async Task Can_create_admin_connection_with_data_source()
{
- using var _ = ((MySqlTestStore)MySqlNorthwindTestStoreFactory.Instance
+ await using var _ = await ((MySqlTestStore)MySqlNorthwindTestStoreFactory.Instance
.GetOrCreate("ConnectionTest"))
- .Initialize(null, (Func)null);
+ .InitializeAsync(null, (Func)null);
- using var dataSource = new MySqlDataSourceBuilder(MySqlTestStore.CreateConnectionString("ConnectionTest")).Build();
+ await using var dataSource = new MySqlDataSourceBuilder(MySqlTestStore.CreateConnectionString("ConnectionTest")).Build();
var optionsBuilder = new DbContextOptionsBuilder();
optionsBuilder.UseMySql(dataSource, AppConfig.ServerVersion, b => b.ApplyConfiguration());
- using var context = new GeneralOptionsContext(optionsBuilder.Options);
+ await using var context = new GeneralOptionsContext(optionsBuilder.Options);
var relationalConnection = context.GetService();
- using var masterConnection = relationalConnection.CreateMasterConnection();
+ await using var masterConnection = relationalConnection.CreateMasterConnection();
Assert.Equal(string.Empty, new MySqlConnectionStringBuilder(masterConnection.ConnectionString).Database);
- masterConnection.Open();
+ await masterConnection.OpenAsync(default);
}
[Fact]
- public void Can_create_admin_connection_with_connection_string()
+ public async Task Can_create_admin_connection_with_connection_string()
{
- using var _ = ((MySqlTestStore)MySqlNorthwindTestStoreFactory.Instance
+ await using var _ = await ((MySqlTestStore)MySqlNorthwindTestStoreFactory.Instance
.GetOrCreate("ConnectionTest"))
- .Initialize(null, (Func)null);
+ .InitializeAsync(null, (Func)null);
var optionsBuilder = new DbContextOptionsBuilder();
optionsBuilder.UseMySql(MySqlTestStore.CreateConnectionString("ConnectionTest"), AppConfig.ServerVersion, b => b.ApplyConfiguration());
- using var context = new GeneralOptionsContext(optionsBuilder.Options);
+ await using var context = new GeneralOptionsContext(optionsBuilder.Options);
var relationalConnection = context.GetService();
- using var masterConnection = relationalConnection.CreateMasterConnection();
+ await using var masterConnection = relationalConnection.CreateMasterConnection();
Assert.Equal(string.Empty, new MySqlConnectionStringBuilder(masterConnection.ConnectionString).Database);
- masterConnection.Open();
+ await masterConnection.OpenAsync(default);
}
[Fact]
- public void Can_create_admin_connection_with_connection()
+ public async Task Can_create_admin_connection_with_connection()
{
- using var _ = ((MySqlTestStore)MySqlNorthwindTestStoreFactory.Instance
+ await using var _ = await ((MySqlTestStore)MySqlNorthwindTestStoreFactory.Instance
.GetOrCreate("ConnectionTest"))
- .Initialize(null, (Func)null);
+ .InitializeAsync(null, (Func)null);
- using var connection = new MySqlConnection(MySqlTestStore.CreateConnectionString("ConnectionTest"));
+ await using var connection = new MySqlConnection(MySqlTestStore.CreateConnectionString("ConnectionTest"));
connection.Open();
var optionsBuilder = new DbContextOptionsBuilder();
optionsBuilder.UseMySql(connection, AppConfig.ServerVersion, b => b.ApplyConfiguration());
- using var context = new GeneralOptionsContext(optionsBuilder.Options);
+ await using var context = new GeneralOptionsContext(optionsBuilder.Options);
var relationalConnection = context.GetService();
- using var masterConnection = relationalConnection.CreateMasterConnection();
+ await using var masterConnection = relationalConnection.CreateMasterConnection();
Assert.Equal(string.Empty, new MySqlConnectionStringBuilder(masterConnection.ConnectionString).Database);
- masterConnection.Open();
+ await masterConnection.OpenAsync(default);
}
[Fact]
diff --git a/test/EFCore.MySql.FunctionalTests/ConvertToProviderTypesMySqlTest.cs b/test/EFCore.MySql.FunctionalTests/ConvertToProviderTypesMySqlTest.cs
index 2b6399ed7..5cecfec35 100644
--- a/test/EFCore.MySql.FunctionalTests/ConvertToProviderTypesMySqlTest.cs
+++ b/test/EFCore.MySql.FunctionalTests/ConvertToProviderTypesMySqlTest.cs
@@ -2,6 +2,7 @@
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
+using System.Threading.Tasks;
using Pomelo.EntityFrameworkCore.MySql.FunctionalTests.TestUtilities;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
@@ -23,8 +24,9 @@ public ConvertToProviderTypesMySqlTest(ConvertToProviderTypesMySqlFixture fixtur
//fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper);
}
- public override void Can_perform_query_with_ansi_strings_test()
+ public override Task Can_perform_query_with_ansi_strings_test()
{
+ return Task.CompletedTask;
}
// TODO: Needed to customize:
@@ -33,7 +35,7 @@ public override void Can_perform_query_with_ansi_strings_test()
#region https://github.com/dotnet/efcore/issues/26068
[ConditionalFact]
- public override void Can_insert_and_read_back_all_non_nullable_data_types()
+ public override async Task Can_insert_and_read_back_all_non_nullable_data_types()
{
using (var context = CreateContext())
{
@@ -68,12 +70,12 @@ public override void Can_insert_and_read_back_all_non_nullable_data_types()
EnumS8 = EnumS8.SomeValue
});
- Assert.Equal(1, context.SaveChanges());
+ Assert.Equal(1, await context.SaveChangesAsync());
}
using (var context = CreateContext())
{
- var dt = context.Set().Where(e => e.Id == 1).ToList().Single();
+ var dt = (await context.Set().Where(e => e.Id == 1).ToListAsync()).Single();
var entityType = context.Model.FindEntityType(typeof(BuiltInDataTypes));
AssertEqualIfMapped(entityType, (short)-1234, () => dt.TestInt16);
@@ -107,7 +109,7 @@ public override void Can_insert_and_read_back_all_non_nullable_data_types()
}
[ConditionalFact]
- public override void Can_insert_and_read_back_non_nullable_backed_data_types()
+ public override async Task Can_insert_and_read_back_non_nullable_backed_data_types()
{
using (var context = CreateContext())
{
@@ -142,12 +144,12 @@ public override void Can_insert_and_read_back_non_nullable_backed_data_types()
EnumS8 = EnumS8.SomeValue
});
- Assert.Equal(1, context.SaveChanges());
+ Assert.Equal(1, await context.SaveChangesAsync());
}
using (var context = CreateContext())
{
- var dt = context.Set().Where(ndt => ndt.Id == 101).ToList().Single();
+ var dt = (await context.Set().Where(ndt => ndt.Id == 101).ToListAsync()).Single();
var entityType = context.Model.FindEntityType(typeof(NonNullableBackedDataTypes));
AssertEqualIfMapped(entityType, (short)-1234, () => dt.Int16);
@@ -181,7 +183,7 @@ public override void Can_insert_and_read_back_non_nullable_backed_data_types()
}
[ConditionalFact]
- public override void Can_insert_and_read_back_nullable_backed_data_types()
+ public override async Task Can_insert_and_read_back_nullable_backed_data_types()
{
using (var context = CreateContext())
{
@@ -216,12 +218,12 @@ public override void Can_insert_and_read_back_nullable_backed_data_types()
EnumS8 = EnumS8.SomeValue
});
- Assert.Equal(1, context.SaveChanges());
+ Assert.Equal(1, await context.SaveChangesAsync());
}
using (var context = CreateContext())
{
- var dt = context.Set().Where(ndt => ndt.Id == 101).ToList().Single();
+ var dt = (await context.Set().Where(ndt => ndt.Id == 101).ToListAsync()).Single();
var entityType = context.Model.FindEntityType(typeof(NullableBackedDataTypes));
AssertEqualIfMapped(entityType, (short)-1234, () => dt.Int16);
@@ -254,7 +256,7 @@ public override void Can_insert_and_read_back_nullable_backed_data_types()
}
[ConditionalFact]
- public override void Can_insert_and_read_back_object_backed_data_types()
+ public override async Task Can_insert_and_read_back_object_backed_data_types()
{
using (var context = CreateContext())
{
@@ -291,12 +293,12 @@ public override void Can_insert_and_read_back_object_backed_data_types()
EnumS8 = EnumS8.SomeValue
});
- Assert.Equal(1, context.SaveChanges());
+ Assert.Equal(1, await context.SaveChangesAsync());
}
using (var context = CreateContext())
{
- var dt = context.Set().Where(ndt => ndt.Id == 101).ToList().Single();
+ var dt = (await context.Set().Where(ndt => ndt.Id == 101).ToListAsync()).Single();
var entityType = context.Model.FindEntityType(typeof(ObjectBackedDataTypes));
AssertEqualIfMapped(entityType, "TestString", () => dt.String);
diff --git a/test/EFCore.MySql.FunctionalTests/CustomConvertersMySqlTest.cs b/test/EFCore.MySql.FunctionalTests/CustomConvertersMySqlTest.cs
index e24a71e29..fa80aacb0 100644
--- a/test/EFCore.MySql.FunctionalTests/CustomConvertersMySqlTest.cs
+++ b/test/EFCore.MySql.FunctionalTests/CustomConvertersMySqlTest.cs
@@ -2,6 +2,7 @@
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
+using System.Threading.Tasks;
using Pomelo.EntityFrameworkCore.MySql.FunctionalTests.TestUtilities;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
@@ -32,7 +33,7 @@ public override void Value_conversion_on_enum_collection_contains()
#region https://github.com/dotnet/efcore/issues/26068
[ConditionalFact]
- public override void Can_insert_and_read_back_all_non_nullable_data_types()
+ public override async Task Can_insert_and_read_back_all_non_nullable_data_types()
{
using (var context = CreateContext())
{
@@ -67,12 +68,12 @@ public override void Can_insert_and_read_back_all_non_nullable_data_types()
EnumS8 = EnumS8.SomeValue
});
- Assert.Equal(1, context.SaveChanges());
+ Assert.Equal(1, await context.SaveChangesAsync());
}
using (var context = CreateContext())
{
- var dt = context.Set().Where(e => e.Id == 1).ToList().Single();
+ var dt = (await context.Set().Where(e => e.Id == 1).ToListAsync()).Single();
var entityType = context.Model.FindEntityType(typeof(BuiltInDataTypes));
AssertEqualIfMapped(entityType, (short)-1234, () => dt.TestInt16);
@@ -106,7 +107,7 @@ public override void Can_insert_and_read_back_all_non_nullable_data_types()
}
[ConditionalFact]
- public override void Can_insert_and_read_back_non_nullable_backed_data_types()
+ public override async Task Can_insert_and_read_back_non_nullable_backed_data_types()
{
using (var context = CreateContext())
{
@@ -141,12 +142,12 @@ public override void Can_insert_and_read_back_non_nullable_backed_data_types()
EnumS8 = EnumS8.SomeValue
});
- Assert.Equal(1, context.SaveChanges());
+ Assert.Equal(1, await context.SaveChangesAsync());
}
using (var context = CreateContext())
{
- var dt = context.Set().Where(ndt => ndt.Id == 101).ToList().Single();
+ var dt = (await context.Set().Where(ndt => ndt.Id == 101).ToListAsync()).Single();
var entityType = context.Model.FindEntityType(typeof(NonNullableBackedDataTypes));
AssertEqualIfMapped(entityType, (short)-1234, () => dt.Int16);
@@ -180,7 +181,7 @@ public override void Can_insert_and_read_back_non_nullable_backed_data_types()
}
[ConditionalFact]
- public override void Can_insert_and_read_back_nullable_backed_data_types()
+ public override async Task Can_insert_and_read_back_nullable_backed_data_types()
{
using (var context = CreateContext())
{
@@ -215,12 +216,12 @@ public override void Can_insert_and_read_back_nullable_backed_data_types()
EnumS8 = EnumS8.SomeValue
});
- Assert.Equal(1, context.SaveChanges());
+ Assert.Equal(1, await context.SaveChangesAsync());
}
using (var context = CreateContext())
{
- var dt = context.Set().Where(ndt => ndt.Id == 101).ToList().Single();
+ var dt = (await context.Set().Where(ndt => ndt.Id == 101).ToListAsync()).Single();
var entityType = context.Model.FindEntityType(typeof(NullableBackedDataTypes));
AssertEqualIfMapped(entityType, (short)-1234, () => dt.Int16);
@@ -253,7 +254,7 @@ public override void Can_insert_and_read_back_nullable_backed_data_types()
}
[ConditionalFact]
- public override void Can_insert_and_read_back_object_backed_data_types()
+ public override async Task Can_insert_and_read_back_object_backed_data_types()
{
using (var context = CreateContext())
{
@@ -290,12 +291,12 @@ public override void Can_insert_and_read_back_object_backed_data_types()
EnumS8 = EnumS8.SomeValue
});
- Assert.Equal(1, context.SaveChanges());
+ Assert.Equal(1, await context.SaveChangesAsync());
}
using (var context = CreateContext())
{
- var dt = context.Set().Where(ndt => ndt.Id == 101).ToList().Single();
+ var dt = (await context.Set().Where(ndt => ndt.Id == 101).ToListAsync()).Single();
var entityType = context.Model.FindEntityType(typeof(ObjectBackedDataTypes));
AssertEqualIfMapped(entityType, "TestString", () => dt.String);
diff --git a/test/EFCore.MySql.FunctionalTests/DataAnnotationMySqlTest.cs b/test/EFCore.MySql.FunctionalTests/DataAnnotationMySqlTest.cs
index 30bf92ff5..3c5942cc8 100644
--- a/test/EFCore.MySql.FunctionalTests/DataAnnotationMySqlTest.cs
+++ b/test/EFCore.MySql.FunctionalTests/DataAnnotationMySqlTest.cs
@@ -1,3 +1,4 @@
+using System.Threading.Tasks;
using Pomelo.EntityFrameworkCore.MySql.FunctionalTests.TestUtilities;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
@@ -109,9 +110,9 @@ public override IModel TableNameAttribute_affects_table_name_in_TPH()
return model;
}
- public override void ConcurrencyCheckAttribute_throws_if_value_in_database_changed()
+ public override async Task ConcurrencyCheckAttribute_throws_if_value_in_database_changed()
{
- base.ConcurrencyCheckAttribute_throws_if_value_in_database_changed();
+ await base.ConcurrencyCheckAttribute_throws_if_value_in_database_changed();
AssertSql(
"""
@@ -153,9 +154,9 @@ LIMIT 1
""");
}
- public override void DatabaseGeneratedAttribute_autogenerates_values_when_set_to_identity()
+ public override async Task DatabaseGeneratedAttribute_autogenerates_values_when_set_to_identity()
{
- base.DatabaseGeneratedAttribute_autogenerates_values_when_set_to_identity();
+ await base.DatabaseGeneratedAttribute_autogenerates_values_when_set_to_identity();
if (AppConfig.ServerVersion.Supports.Returning)
{
diff --git a/test/EFCore.MySql.FunctionalTests/EFCore.MySql.FunctionalTests.csproj b/test/EFCore.MySql.FunctionalTests/EFCore.MySql.FunctionalTests.csproj
index 668dae678..c5295a16f 100644
--- a/test/EFCore.MySql.FunctionalTests/EFCore.MySql.FunctionalTests.csproj
+++ b/test/EFCore.MySql.FunctionalTests/EFCore.MySql.FunctionalTests.csproj
@@ -7,7 +7,7 @@
true
$(DefaultItemExcludes);*.trx
-
+
false
false
@@ -29,10 +29,6 @@
-
-
-
-
@@ -75,11 +71,17 @@
$(LocalEFCoreRepository)\artifacts\bin\EFCore.Design.Tests\Debug\$(EfCoreTestTargetFramework)\Microsoft.EntityFrameworkCore.Design.dll
+
+
+
+
+
+
+
-
-
+
diff --git a/test/EFCore.MySql.FunctionalTests/ExistingConnectionMySqlTest.cs b/test/EFCore.MySql.FunctionalTests/ExistingConnectionMySqlTest.cs
index 2e83de1d5..297496bfc 100644
--- a/test/EFCore.MySql.FunctionalTests/ExistingConnectionMySqlTest.cs
+++ b/test/EFCore.MySql.FunctionalTests/ExistingConnectionMySqlTest.cs
@@ -23,16 +23,16 @@ private static async Task Can_use_an_existing_closed_connection(bool openConnect
.AddEntityFrameworkMySql()
.BuildServiceProvider();
- using (var store = (MySqlTestStore)MySqlNorthwindTestStoreFactory.Instance
- .GetOrCreate(null)
- .Initialize(null, (Func)null))
+ await using (var store = (MySqlTestStore)await MySqlNorthwindTestStoreFactory.Instance
+ .GetOrCreate(null)
+ .InitializeAsync(null, (Func)null))
{
store.CloseConnection();
var openCount = 0;
var closeCount = 0;
- using (var connection = new MySqlConnection(store.ConnectionString))
+ await using (var connection = new MySqlConnection(store.ConnectionString))
{
if (openConnection)
{
@@ -56,7 +56,7 @@ private static async Task Can_use_an_existing_closed_connection(bool openConnect
.UseInternalServiceProvider(serviceProvider)
.Options;
- using (var context = new NorthwindContext(options))
+ await using (var context = new NorthwindContext(options))
{
Assert.Equal(91, await context.Customers.CountAsync());
}
@@ -84,9 +84,9 @@ private static async Task Closed_connection_missing_AllowUserVariables_true_in_o
.AddEntityFrameworkMySql()
.BuildServiceProvider();
- await using (var store = (MySqlTestStore)MySqlNorthwindTestStoreFactory.Instance
+ await using (var store = (MySqlTestStore)await MySqlNorthwindTestStoreFactory.Instance
.GetOrCreate(null)
- .Initialize(null, (Func)null))
+ .InitializeAsync(null, (Func)null))
{
store.CloseConnection();
@@ -99,7 +99,7 @@ private static async Task Closed_connection_missing_AllowUserVariables_true_in_o
.UseInternalServiceProvider(serviceProvider)
.Options;
- using var context = new NorthwindContext(options);
+ await using var context = new NorthwindContext(options);
Assert.Equal(91, await context.Customers.CountAsync());
}
@@ -109,9 +109,9 @@ private static async Task Closed_connection_missing_AllowUserVariables_true_in_o
[Fact]
private static async Task Opened_connection_missing_AllowUserVariables_true_in_original_connection_string_throws()
{
- await using (var store = (MySqlTestStore)MySqlNorthwindTestStoreFactory.Instance
+ await using (var store = (MySqlTestStore)await MySqlNorthwindTestStoreFactory.Instance
.GetOrCreate(null)
- .Initialize(null, (Func)null))
+ .InitializeAsync(null, (Func)null))
{
store.CloseConnection();
@@ -145,9 +145,9 @@ private static async Task Closed_connection_missing_UseAffectedRows_false_in_ori
.AddEntityFrameworkMySql()
.BuildServiceProvider();
- await using (var store = (MySqlTestStore)MySqlNorthwindTestStoreFactory.Instance
+ await using (var store = (MySqlTestStore)await MySqlNorthwindTestStoreFactory.Instance
.GetOrCreate(null)
- .Initialize(null, (Func)null))
+ .InitializeAsync(null, (Func)null))
{
store.CloseConnection();
@@ -160,7 +160,7 @@ private static async Task Closed_connection_missing_UseAffectedRows_false_in_ori
.UseInternalServiceProvider(serviceProvider)
.Options;
- using var context = new NorthwindContext(options);
+ await using var context = new NorthwindContext(options);
Assert.Equal(91, await context.Customers.CountAsync());
}
@@ -170,9 +170,9 @@ private static async Task Closed_connection_missing_UseAffectedRows_false_in_ori
[Fact]
private static async Task Opened_connection_missing_UseAffectedRows_false_in_original_connection_string_throws()
{
- await using (var store = (MySqlTestStore)MySqlNorthwindTestStoreFactory.Instance
+ await using (var store = (MySqlTestStore)await MySqlNorthwindTestStoreFactory.Instance
.GetOrCreate(null)
- .Initialize(null, (Func)null))
+ .InitializeAsync(null, (Func)null))
{
store.CloseConnection();
diff --git a/test/EFCore.MySql.FunctionalTests/FullMigrationsMySqlTest.cs b/test/EFCore.MySql.FunctionalTests/FullMigrationsMySqlTest.cs
index 4fa81e43c..977dae823 100644
--- a/test/EFCore.MySql.FunctionalTests/FullMigrationsMySqlTest.cs
+++ b/test/EFCore.MySql.FunctionalTests/FullMigrationsMySqlTest.cs
@@ -48,15 +48,17 @@ public virtual void Can_create_stored_procedure_script_without_custom_delimiter_
fromMigration: Migration.InitialDatabase,
toMigration: "00000000000002_MigrationPrimaryKeyChange2"));
+ // TODO: 9.0
+ // Pomelo helper stored procedure statements should be inside the transaction scope.
Assert.Equal(
- @"CREATE TABLE IF NOT EXISTS `__EFMigrationsHistory` (
+"""
+CREATE TABLE IF NOT EXISTS `__EFMigrationsHistory` (
`MigrationId` varchar(150) CHARACTER SET utf8mb4 NOT NULL,
`ProductVersion` varchar(32) CHARACTER SET utf8mb4 NOT NULL,
CONSTRAINT `PK___EFMigrationsHistory` PRIMARY KEY (`MigrationId`)
) CHARACTER SET=utf8mb4;
START TRANSACTION;
-
CREATE TABLE `Table1` (
`Id` int NOT NULL,
`AlternatePK` int NOT NULL,
@@ -66,10 +68,6 @@ public virtual void Can_create_stored_procedure_script_without_custom_delimiter_
INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
VALUES ('00000000000001_MigrationPrimaryKeyChange1', '7.0.0-test');
-COMMIT;
-
-START TRANSACTION;
-
DROP PROCEDURE IF EXISTS `POMELO_BEFORE_DROP_PRIMARY_KEY`;
CREATE PROCEDURE `POMELO_BEFORE_DROP_PRIMARY_KEY`(IN `SCHEMA_NAME_ARGUMENT` VARCHAR(255), IN `TABLE_NAME_ARGUMENT` VARCHAR(255))
BEGIN
@@ -159,7 +157,8 @@ INTO PRIMARY_KEY_COLUMN_NAME
DROP PROCEDURE `POMELO_AFTER_ADD_PRIMARY_KEY`;
COMMIT;
-",
+
+""",
Sql,
ignoreLineEndingDifferences: true);
}
@@ -188,7 +187,8 @@ public virtual void Can_generate_idempotent_up_scripts_with_primary_key_related_
h => Assert.Equal("00000000000002_MigrationPrimaryKeyChange2", h.MigrationId));
Assert.Equal(
- @"DROP PROCEDURE IF EXISTS `POMELO_BEFORE_DROP_PRIMARY_KEY`;
+"""
+DROP PROCEDURE IF EXISTS `POMELO_BEFORE_DROP_PRIMARY_KEY`;
DELIMITER //
CREATE PROCEDURE `POMELO_BEFORE_DROP_PRIMARY_KEY`(IN `SCHEMA_NAME_ARGUMENT` VARCHAR(255), IN `TABLE_NAME_ARGUMENT` VARCHAR(255))
BEGIN
@@ -277,7 +277,6 @@ INTO PRIMARY_KEY_COLUMN_NAME
) CHARACTER SET=utf8mb4;
START TRANSACTION;
-
DROP PROCEDURE IF EXISTS MigrationsScript;
DELIMITER //
CREATE PROCEDURE MigrationsScript()
@@ -311,10 +310,6 @@ IF NOT EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000
CALL MigrationsScript();
DROP PROCEDURE MigrationsScript;
-COMMIT;
-
-START TRANSACTION;
-
DROP PROCEDURE IF EXISTS MigrationsScript;
DELIMITER //
CREATE PROCEDURE MigrationsScript()
@@ -366,7 +361,8 @@ IF NOT EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000
DROP PROCEDURE `POMELO_AFTER_ADD_PRIMARY_KEY`;
-",
+
+""",
Sql,
ignoreLineEndingDifferences: true);
}
@@ -394,14 +390,14 @@ public virtual void Drop_primary_key_with_recreating_foreign_keys()
h => Assert.Equal("00000000000002_MigrationDropPrimaryKeyWithRecreatingForeignKeys2", h.MigrationId));
Assert.Equal(
- @"CREATE TABLE IF NOT EXISTS `__EFMigrationsHistory` (
+"""
+CREATE TABLE IF NOT EXISTS `__EFMigrationsHistory` (
`MigrationId` varchar(150) CHARACTER SET utf8mb4 NOT NULL,
`ProductVersion` varchar(32) CHARACTER SET utf8mb4 NOT NULL,
CONSTRAINT `PK___EFMigrationsHistory` PRIMARY KEY (`MigrationId`)
) CHARACTER SET=utf8mb4;
START TRANSACTION;
-
CREATE TABLE `Foo` (
`FooId` int NOT NULL,
CONSTRAINT `PK_Foo` PRIMARY KEY (`FooId`)
@@ -423,10 +419,6 @@ public virtual void Drop_primary_key_with_recreating_foreign_keys()
INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
VALUES ('00000000000001_MigrationDropPrimaryKeyWithRecreatingForeignKeys1', '7.0.0-test');
-COMMIT;
-
-START TRANSACTION;
-
DROP PROCEDURE IF EXISTS `POMELO_BEFORE_DROP_PRIMARY_KEY`;
DELIMITER //
CREATE PROCEDURE `POMELO_BEFORE_DROP_PRIMARY_KEY`(IN `SCHEMA_NAME_ARGUMENT` VARCHAR(255), IN `TABLE_NAME_ARGUMENT` VARCHAR(255))
@@ -533,7 +525,8 @@ INTO PRIMARY_KEY_COLUMN_NAME
COMMIT;
-",
+
+""",
Sql,
ignoreLineEndingDifferences: true);
}
diff --git a/test/EFCore.MySql.FunctionalTests/GraphUpdatesMySqlTestBase.cs b/test/EFCore.MySql.FunctionalTests/GraphUpdatesMySqlTestBase.cs
index e84d4f7eb..1cd0aa7d5 100644
--- a/test/EFCore.MySql.FunctionalTests/GraphUpdatesMySqlTestBase.cs
+++ b/test/EFCore.MySql.FunctionalTests/GraphUpdatesMySqlTestBase.cs
@@ -63,6 +63,24 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con
modelBuilder.Entity().Property("CategoryId").HasDefaultValue(1);
modelBuilder.Entity().Property(e => e.CategoryId).HasDefaultValue(2);
+
+ modelBuilder.Entity>(
+ b =>
+ {
+ b.Property(e => e.PrimaryGroup).HasDefaultValue(1).HasSentinel(1);
+ });
+
+ modelBuilder.Entity>(
+ b =>
+ {
+ b.Property(e => e.PrimaryGroup).HasDefaultValue(true);
+ });
+
+ modelBuilder.Entity>(
+ b =>
+ {
+ b.Property(e => e.PrimaryGroup).HasDefaultValue(true);
+ });
}
}
}
diff --git a/test/EFCore.MySql.FunctionalTests/ManyToManyLoadMySqlTest.cs b/test/EFCore.MySql.FunctionalTests/ManyToManyLoadMySqlTest.cs
index b5a68412c..f6f3e2158 100644
--- a/test/EFCore.MySql.FunctionalTests/ManyToManyLoadMySqlTest.cs
+++ b/test/EFCore.MySql.FunctionalTests/ManyToManyLoadMySqlTest.cs
@@ -13,7 +13,7 @@ public ManyToManyLoadMySqlTest(ManyToManyLoadMySqlFixture fixture)
{
}
- public class ManyToManyLoadMySqlFixture : ManyToManyLoadFixtureBase
+ public class ManyToManyLoadMySqlFixture : ManyToManyLoadFixtureBase, ITestSqlLoggerFactory
{
public TestSqlLoggerFactory TestSqlLoggerFactory => (TestSqlLoggerFactory)ListLoggerFactory;
protected override ITestStoreFactory TestStoreFactory => MySqlTestStoreFactory.Instance;
diff --git a/test/EFCore.MySql.FunctionalTests/ManyToManyTrackingMySqlTestBase.cs b/test/EFCore.MySql.FunctionalTests/ManyToManyTrackingMySqlTestBase.cs
index 82fd8de7f..b1978819f 100644
--- a/test/EFCore.MySql.FunctionalTests/ManyToManyTrackingMySqlTestBase.cs
+++ b/test/EFCore.MySql.FunctionalTests/ManyToManyTrackingMySqlTestBase.cs
@@ -26,8 +26,10 @@ protected ManyToManyTrackingMySqlTestBase(TFixture fixture)
protected override void UseTransaction(DatabaseFacade facade, IDbContextTransaction transaction)
=> facade.UseTransaction(transaction.GetDbTransaction());
- public class ManyToManyTrackingMySqlFixtureBase : ManyToManyTrackingRelationalFixture
+ public class ManyToManyTrackingMySqlFixtureBase : ManyToManyTrackingRelationalFixture, ITestSqlLoggerFactory
{
+ public TestSqlLoggerFactory TestSqlLoggerFactory => (TestSqlLoggerFactory)ListLoggerFactory;
+
protected override ITestStoreFactory TestStoreFactory => MySqlTestStoreFactory.Instance;
protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext context)
diff --git a/test/EFCore.MySql.FunctionalTests/MigrationsInfrastructureMySqlTest.cs b/test/EFCore.MySql.FunctionalTests/MigrationsInfrastructureMySqlTest.cs
index ba0b81774..2dbdc139d 100644
--- a/test/EFCore.MySql.FunctionalTests/MigrationsInfrastructureMySqlTest.cs
+++ b/test/EFCore.MySql.FunctionalTests/MigrationsInfrastructureMySqlTest.cs
@@ -52,524 +52,127 @@ public override void Can_generate_no_migration_script()
ignoreLineEndingDifferences: true);
}
- public override void Can_generate_up_scripts()
+ public override void Can_apply_one_migration()
{
- base.Can_generate_up_scripts();
+ base.Can_apply_one_migration();
- Assert.Equal(
- @"CREATE TABLE IF NOT EXISTS `__EFMigrationsHistory` (
- `MigrationId` varchar(150) CHARACTER SET utf8mb4 NOT NULL,
- `ProductVersion` varchar(32) CHARACTER SET utf8mb4 NOT NULL,
- CONSTRAINT `PK___EFMigrationsHistory` PRIMARY KEY (`MigrationId`)
-) CHARACTER SET=utf8mb4;
-
-START TRANSACTION;
-
-CREATE TABLE `Table1` (
- `Id` int NOT NULL,
- `Foo` int NOT NULL,
- `Description` longtext NOT NULL,
- CONSTRAINT `PK_Table1` PRIMARY KEY (`Id`)
-);
-
-INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
-VALUES ('00000000000001_Migration1', '7.0.0-test');
-
-COMMIT;
-
-START TRANSACTION;
-
-ALTER TABLE `Table1` RENAME COLUMN `Foo` TO `Bar`;
-
-INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
-VALUES ('00000000000002_Migration2', '7.0.0-test');
-
-COMMIT;
-
-START TRANSACTION;
-
-INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
-VALUES ('00000000000003_Migration3', '7.0.0-test');
-
-COMMIT;
-
-START TRANSACTION;
-
-INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
-VALUES ('00000000000004_Migration4', '7.0.0-test');
-
-COMMIT;
-
-START TRANSACTION;
-
-INSERT INTO Table1 (Id, Bar, Description) VALUES (-1, ' ', 'Value With
-
-Empty Lines')
-
-INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
-VALUES ('00000000000005_Migration5', '7.0.0-test');
-
-COMMIT;
-
-START TRANSACTION;
-
-INSERT INTO Table1 (Id, Bar, Description) VALUES (-2, ' ', 'GO
-Value With
-
-Empty Lines')
-
-INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
-VALUES ('00000000000006_Migration6', '7.0.0-test');
-
-COMMIT;
-
-START TRANSACTION;
-
-INSERT INTO Table1 (Id, Bar, Description) VALUES (-3, ' ', 'GO
-Value With
-
-GO
-
-Empty Lines
-GO')
-
-INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
-VALUES ('00000000000007_Migration7', '7.0.0-test');
-
-COMMIT;
-
-",
- Sql,
- ignoreLineEndingDifferences: true);
+ Assert.Null(Sql);
}
- public override void Can_generate_one_up_script()
+ public override void Can_apply_one_migration_in_parallel()
{
- base.Can_generate_one_up_script();
-
- Assert.Equal(
- @"START TRANSACTION;
-
-ALTER TABLE `Table1` RENAME COLUMN `Foo` TO `Bar`;
+ base.Can_apply_one_migration_in_parallel();
-INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
-VALUES ('00000000000002_Migration2', '7.0.0-test');
-
-COMMIT;
-
-",
- Sql,
- ignoreLineEndingDifferences: true);
+ Assert.Null(Sql);
}
- public override void Can_generate_up_script_using_names()
+ public override void Can_apply_second_migration_in_parallel()
{
- base.Can_generate_up_script_using_names();
+ base.Can_apply_second_migration_in_parallel();
- Assert.Equal(
- @"START TRANSACTION;
-
-ALTER TABLE `Table1` RENAME COLUMN `Foo` TO `Bar`;
-
-INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
-VALUES ('00000000000002_Migration2', '7.0.0-test');
-
-COMMIT;
-
-",
- Sql,
- ignoreLineEndingDifferences: true);
+ Assert.Null(Sql);
}
- public override void Can_generate_idempotent_up_scripts()
+ public override async Task Can_apply_one_migration_in_parallel_async()
{
- base.Can_generate_idempotent_up_scripts();
+ await base.Can_apply_one_migration_in_parallel_async();
- Assert.Equal(@"CREATE TABLE IF NOT EXISTS `__EFMigrationsHistory` (
- `MigrationId` varchar(150) CHARACTER SET utf8mb4 NOT NULL,
- `ProductVersion` varchar(32) CHARACTER SET utf8mb4 NOT NULL,
- CONSTRAINT `PK___EFMigrationsHistory` PRIMARY KEY (`MigrationId`)
-) CHARACTER SET=utf8mb4;
-
-START TRANSACTION;
-
-DROP PROCEDURE IF EXISTS MigrationsScript;
-DELIMITER //
-CREATE PROCEDURE MigrationsScript()
-BEGIN
- IF NOT EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000000000001_Migration1') THEN
-
- CREATE TABLE `Table1` (
- `Id` int NOT NULL,
- `Foo` int NOT NULL,
- `Description` longtext NOT NULL,
- CONSTRAINT `PK_Table1` PRIMARY KEY (`Id`)
- );
-
- END IF;
-END //
-DELIMITER ;
-CALL MigrationsScript();
-DROP PROCEDURE MigrationsScript;
-
-DROP PROCEDURE IF EXISTS MigrationsScript;
-DELIMITER //
-CREATE PROCEDURE MigrationsScript()
-BEGIN
- IF NOT EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000000000001_Migration1') THEN
-
- INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
- VALUES ('00000000000001_Migration1', '7.0.0-test');
-
- END IF;
-END //
-DELIMITER ;
-CALL MigrationsScript();
-DROP PROCEDURE MigrationsScript;
-
-COMMIT;
-
-START TRANSACTION;
-
-DROP PROCEDURE IF EXISTS MigrationsScript;
-DELIMITER //
-CREATE PROCEDURE MigrationsScript()
-BEGIN
- IF NOT EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000000000002_Migration2') THEN
-
- ALTER TABLE `Table1` RENAME COLUMN `Foo` TO `Bar`;
-
- END IF;
-END //
-DELIMITER ;
-CALL MigrationsScript();
-DROP PROCEDURE MigrationsScript;
-
-DROP PROCEDURE IF EXISTS MigrationsScript;
-DELIMITER //
-CREATE PROCEDURE MigrationsScript()
-BEGIN
- IF NOT EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000000000002_Migration2') THEN
-
- INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
- VALUES ('00000000000002_Migration2', '7.0.0-test');
-
- END IF;
-END //
-DELIMITER ;
-CALL MigrationsScript();
-DROP PROCEDURE MigrationsScript;
-
-COMMIT;
-
-START TRANSACTION;
-
-DROP PROCEDURE IF EXISTS MigrationsScript;
-DELIMITER //
-CREATE PROCEDURE MigrationsScript()
-BEGIN
- IF NOT EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000000000003_Migration3') THEN
-
- INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
- VALUES ('00000000000003_Migration3', '7.0.0-test');
-
- END IF;
-END //
-DELIMITER ;
-CALL MigrationsScript();
-DROP PROCEDURE MigrationsScript;
-
-COMMIT;
-
-START TRANSACTION;
-
-DROP PROCEDURE IF EXISTS MigrationsScript;
-DELIMITER //
-CREATE PROCEDURE MigrationsScript()
-BEGIN
- IF NOT EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000000000004_Migration4') THEN
-
- INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
- VALUES ('00000000000004_Migration4', '7.0.0-test');
-
- END IF;
-END //
-DELIMITER ;
-CALL MigrationsScript();
-DROP PROCEDURE MigrationsScript;
-
-COMMIT;
-
-START TRANSACTION;
-
-DROP PROCEDURE IF EXISTS MigrationsScript;
-DELIMITER //
-CREATE PROCEDURE MigrationsScript()
-BEGIN
- IF NOT EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000000000005_Migration5') THEN
-
- INSERT INTO Table1 (Id, Bar, Description) VALUES (-1, ' ', 'Value With
-
- Empty Lines')
-
- END IF;
-END //
-DELIMITER ;
-CALL MigrationsScript();
-DROP PROCEDURE MigrationsScript;
-
-DROP PROCEDURE IF EXISTS MigrationsScript;
-DELIMITER //
-CREATE PROCEDURE MigrationsScript()
-BEGIN
- IF NOT EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000000000005_Migration5') THEN
-
- INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
- VALUES ('00000000000005_Migration5', '7.0.0-test');
-
- END IF;
-END //
-DELIMITER ;
-CALL MigrationsScript();
-DROP PROCEDURE MigrationsScript;
-
-COMMIT;
-
-START TRANSACTION;
-
-DROP PROCEDURE IF EXISTS MigrationsScript;
-DELIMITER //
-CREATE PROCEDURE MigrationsScript()
-BEGIN
- IF NOT EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000000000006_Migration6') THEN
-
- INSERT INTO Table1 (Id, Bar, Description) VALUES (-2, ' ', 'GO
- Value With
-
- Empty Lines')
-
- END IF;
-END //
-DELIMITER ;
-CALL MigrationsScript();
-DROP PROCEDURE MigrationsScript;
-
-DROP PROCEDURE IF EXISTS MigrationsScript;
-DELIMITER //
-CREATE PROCEDURE MigrationsScript()
-BEGIN
- IF NOT EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000000000006_Migration6') THEN
-
- INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
- VALUES ('00000000000006_Migration6', '7.0.0-test');
-
- END IF;
-END //
-DELIMITER ;
-CALL MigrationsScript();
-DROP PROCEDURE MigrationsScript;
-
-COMMIT;
-
-START TRANSACTION;
-
-DROP PROCEDURE IF EXISTS MigrationsScript;
-DELIMITER //
-CREATE PROCEDURE MigrationsScript()
-BEGIN
- IF NOT EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000000000007_Migration7') THEN
-
- INSERT INTO Table1 (Id, Bar, Description) VALUES (-3, ' ', 'GO
- Value With
-
- GO
-
- Empty Lines
- GO')
-
- END IF;
-END //
-DELIMITER ;
-CALL MigrationsScript();
-DROP PROCEDURE MigrationsScript;
-
-DROP PROCEDURE IF EXISTS MigrationsScript;
-DELIMITER //
-CREATE PROCEDURE MigrationsScript()
-BEGIN
- IF NOT EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000000000007_Migration7') THEN
-
- INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
- VALUES ('00000000000007_Migration7', '7.0.0-test');
-
- END IF;
-END //
-DELIMITER ;
-CALL MigrationsScript();
-DROP PROCEDURE MigrationsScript;
-
-COMMIT;
-
-",
- Sql,
- ignoreLineEndingDifferences: true);
+ Assert.Null(Sql);
}
- public override void Can_generate_down_scripts()
+ public override async Task Can_apply_second_migration_in_parallel_async()
{
- base.Can_generate_down_scripts();
-
- Assert.Equal(
- @"START TRANSACTION;
-
-ALTER TABLE `Table1` RENAME COLUMN `Bar` TO `Foo`;
-
-DELETE FROM `__EFMigrationsHistory`
-WHERE `MigrationId` = '00000000000002_Migration2';
-
-COMMIT;
-
-START TRANSACTION;
-
-DROP TABLE `Table1`;
-
-DELETE FROM `__EFMigrationsHistory`
-WHERE `MigrationId` = '00000000000001_Migration1';
-
-COMMIT;
-
-",
- Sql,
- ignoreLineEndingDifferences: true);
- }
-
- public override void Can_generate_one_down_script()
- {
- base.Can_generate_one_down_script();
-
- Assert.Equal(
- @"START TRANSACTION;
-
-ALTER TABLE `Table1` RENAME COLUMN `Bar` TO `Foo`;
-
-DELETE FROM `__EFMigrationsHistory`
-WHERE `MigrationId` = '00000000000002_Migration2';
-
-COMMIT;
-
-",
- Sql,
- ignoreLineEndingDifferences: true);
- }
-
- public override void Can_generate_down_script_using_names()
- {
- base.Can_generate_down_script_using_names();
-
- Assert.Equal(
- @"START TRANSACTION;
-
-ALTER TABLE `Table1` RENAME COLUMN `Bar` TO `Foo`;
-
-DELETE FROM `__EFMigrationsHistory`
-WHERE `MigrationId` = '00000000000002_Migration2';
-
-COMMIT;
-
-",
- Sql,
- ignoreLineEndingDifferences: true);
+ await base.Can_apply_second_migration_in_parallel_async();
+
+ Assert.Null(Sql);
}
- public override void Can_generate_idempotent_down_scripts()
+ public override async Task Can_generate_up_and_down_scripts()
{
- base.Can_generate_idempotent_down_scripts();
+ await base.Can_generate_up_and_down_scripts();
Assert.Equal(
- @"START TRANSACTION;
+"""
+CREATE TABLE IF NOT EXISTS `__EFMigrationsHistory` (
+ `MigrationId` varchar(150) CHARACTER SET utf8mb4 NOT NULL,
+ `ProductVersion` varchar(32) CHARACTER SET utf8mb4 NOT NULL,
+ CONSTRAINT `PK___EFMigrationsHistory` PRIMARY KEY (`MigrationId`)
+) CHARACTER SET=utf8mb4;
-DROP PROCEDURE IF EXISTS MigrationsScript;
-DELIMITER //
-CREATE PROCEDURE MigrationsScript()
-BEGIN
- IF EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000000000002_Migration2') THEN
+START TRANSACTION;
+CREATE TABLE `Table1` (
+ `Id` int NOT NULL,
+ `Foo` int NOT NULL,
+ `Description` longtext NOT NULL,
+ CONSTRAINT `PK_Table1` PRIMARY KEY (`Id`)
+);
- ALTER TABLE `Table1` RENAME COLUMN `Bar` TO `Foo`;
+INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
+VALUES ('00000000000001_Migration1', '7.0.0-test');
- END IF;
-END //
-DELIMITER ;
-CALL MigrationsScript();
-DROP PROCEDURE MigrationsScript;
+ALTER TABLE `Table1` RENAME COLUMN `Foo` TO `Bar`;
-DROP PROCEDURE IF EXISTS MigrationsScript;
-DELIMITER //
-CREATE PROCEDURE MigrationsScript()
-BEGIN
- IF EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000000000002_Migration2') THEN
+INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
+VALUES ('00000000000002_Migration2', '7.0.0-test');
+
+INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
+VALUES ('00000000000003_Migration3', '7.0.0-test');
- DELETE FROM `__EFMigrationsHistory`
- WHERE `MigrationId` = '00000000000002_Migration2';
+INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
+VALUES ('00000000000004_Migration4', '7.0.0-test');
- END IF;
-END //
-DELIMITER ;
-CALL MigrationsScript();
-DROP PROCEDURE MigrationsScript;
+INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
+VALUES ('00000000000005_Migration5', '7.0.0-test');
+
+INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
+VALUES ('00000000000006_Migration6', '7.0.0-test');
+
+INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
+VALUES ('00000000000007_Migration7', '7.0.0-test');
COMMIT;
START TRANSACTION;
+DELETE FROM `__EFMigrationsHistory`
+WHERE `MigrationId` = '00000000000007_Migration7';
-DROP PROCEDURE IF EXISTS MigrationsScript;
-DELIMITER //
-CREATE PROCEDURE MigrationsScript()
-BEGIN
- IF EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000000000001_Migration1') THEN
+DELETE FROM `__EFMigrationsHistory`
+WHERE `MigrationId` = '00000000000006_Migration6';
- DROP TABLE `Table1`;
+DELETE FROM `__EFMigrationsHistory`
+WHERE `MigrationId` = '00000000000005_Migration5';
- END IF;
-END //
-DELIMITER ;
-CALL MigrationsScript();
-DROP PROCEDURE MigrationsScript;
+DELETE FROM `__EFMigrationsHistory`
+WHERE `MigrationId` = '00000000000004_Migration4';
-DROP PROCEDURE IF EXISTS MigrationsScript;
-DELIMITER //
-CREATE PROCEDURE MigrationsScript()
-BEGIN
- IF EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000000000001_Migration1') THEN
+DELETE FROM `__EFMigrationsHistory`
+WHERE `MigrationId` = '00000000000003_Migration3';
- DELETE FROM `__EFMigrationsHistory`
- WHERE `MigrationId` = '00000000000001_Migration1';
+ALTER TABLE `Table1` RENAME COLUMN `Bar` TO `Foo`;
- END IF;
-END //
-DELIMITER ;
-CALL MigrationsScript();
-DROP PROCEDURE MigrationsScript;
+DELETE FROM `__EFMigrationsHistory`
+WHERE `MigrationId` = '00000000000002_Migration2';
+
+DROP TABLE `Table1`;
+
+DELETE FROM `__EFMigrationsHistory`
+WHERE `MigrationId` = '00000000000001_Migration1';
COMMIT;
-",
+
+""",
Sql,
ignoreLineEndingDifferences: true);
}
- public override void Can_get_active_provider()
- {
- base.Can_get_active_provider();
-
- Assert.Equal("Pomelo.EntityFrameworkCore.MySql", ActiveProvider);
- }
-
- public override void Can_generate_up_scripts_noTransactions()
+ public override async Task Can_generate_up_and_down_scripts_noTransactions()
{
- base.Can_generate_up_scripts_noTransactions();
+ await base.Can_generate_up_and_down_scripts_noTransactions();
Assert.Equal(
- @"CREATE TABLE IF NOT EXISTS `__EFMigrationsHistory` (
+"""
+CREATE TABLE IF NOT EXISTS `__EFMigrationsHistory` (
`MigrationId` varchar(150) CHARACTER SET utf8mb4 NOT NULL,
`ProductVersion` varchar(32) CHARACTER SET utf8mb4 NOT NULL,
CONSTRAINT `PK___EFMigrationsHistory` PRIMARY KEY (`MigrationId`)
@@ -596,48 +199,116 @@ public override void Can_generate_up_scripts_noTransactions()
INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
VALUES ('00000000000004_Migration4', '7.0.0-test');
-INSERT INTO Table1 (Id, Bar, Description) VALUES (-1, ' ', 'Value With
+INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
+VALUES ('00000000000005_Migration5', '7.0.0-test');
-Empty Lines')
+INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
+VALUES ('00000000000006_Migration6', '7.0.0-test');
INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
-VALUES ('00000000000005_Migration5', '7.0.0-test');
+VALUES ('00000000000007_Migration7', '7.0.0-test');
+
+DELETE FROM `__EFMigrationsHistory`
+WHERE `MigrationId` = '00000000000007_Migration7';
+
+DELETE FROM `__EFMigrationsHistory`
+WHERE `MigrationId` = '00000000000006_Migration6';
+
+DELETE FROM `__EFMigrationsHistory`
+WHERE `MigrationId` = '00000000000005_Migration5';
+
+DELETE FROM `__EFMigrationsHistory`
+WHERE `MigrationId` = '00000000000004_Migration4';
+
+DELETE FROM `__EFMigrationsHistory`
+WHERE `MigrationId` = '00000000000003_Migration3';
+
+ALTER TABLE `Table1` RENAME COLUMN `Bar` TO `Foo`;
+
+DELETE FROM `__EFMigrationsHistory`
+WHERE `MigrationId` = '00000000000002_Migration2';
+
+DROP TABLE `Table1`;
-INSERT INTO Table1 (Id, Bar, Description) VALUES (-2, ' ', 'GO
-Value With
+DELETE FROM `__EFMigrationsHistory`
+WHERE `MigrationId` = '00000000000001_Migration1';
+
+
+""",
+ Sql,
+ ignoreLineEndingDifferences: true);
+ }
+
+ public override async Task Can_generate_one_up_and_down_script()
+ {
+ await base.Can_generate_one_up_and_down_script();
-Empty Lines')
+ Assert.Equal(
+"""
+START TRANSACTION;
+ALTER TABLE `Table1` RENAME COLUMN `Foo` TO `Bar`;
INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
-VALUES ('00000000000006_Migration6', '7.0.0-test');
+VALUES ('00000000000002_Migration2', '7.0.0-test');
+
+COMMIT;
+
+START TRANSACTION;
+ALTER TABLE `Table1` RENAME COLUMN `Bar` TO `Foo`;
+
+DELETE FROM `__EFMigrationsHistory`
+WHERE `MigrationId` = '00000000000002_Migration2';
+
+COMMIT;
-INSERT INTO Table1 (Id, Bar, Description) VALUES (-3, ' ', 'GO
-Value With
-GO
+""",
+ Sql,
+ ignoreLineEndingDifferences: true);
+ }
+
+ public override async Task Can_generate_up_and_down_script_using_names()
+ {
+ await base.Can_generate_up_and_down_script_using_names();
-Empty Lines
-GO')
+ Assert.Equal(
+"""
+START TRANSACTION;
+ALTER TABLE `Table1` RENAME COLUMN `Foo` TO `Bar`;
INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
-VALUES ('00000000000007_Migration7', '7.0.0-test');
+VALUES ('00000000000002_Migration2', '7.0.0-test');
-",
+COMMIT;
+
+START TRANSACTION;
+ALTER TABLE `Table1` RENAME COLUMN `Bar` TO `Foo`;
+
+DELETE FROM `__EFMigrationsHistory`
+WHERE `MigrationId` = '00000000000002_Migration2';
+
+COMMIT;
+
+
+""",
Sql,
ignoreLineEndingDifferences: true);
}
- public override void Can_generate_idempotent_up_scripts_noTransactions()
+ public override async Task Can_generate_idempotent_up_and_down_scripts()
{
- base.Can_generate_idempotent_up_scripts_noTransactions();
+ var exception = await Assert.ThrowsAsync(() => base.Can_generate_idempotent_up_and_down_scripts());
+ Assert.Equal("'DELIMITER' should not be used with MySqlConnector. See https://mysqlconnector.net/delimiter", exception.Message);
Assert.Equal(
- @"CREATE TABLE IF NOT EXISTS `__EFMigrationsHistory` (
+"""
+CREATE TABLE IF NOT EXISTS `__EFMigrationsHistory` (
`MigrationId` varchar(150) CHARACTER SET utf8mb4 NOT NULL,
`ProductVersion` varchar(32) CHARACTER SET utf8mb4 NOT NULL,
CONSTRAINT `PK___EFMigrationsHistory` PRIMARY KEY (`MigrationId`)
) CHARACTER SET=utf8mb4;
+START TRANSACTION;
DROP PROCEDURE IF EXISTS MigrationsScript;
DELIMITER //
CREATE PROCEDURE MigrationsScript()
@@ -701,45 +372,39 @@ IF NOT EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000
CALL MigrationsScript();
DROP PROCEDURE MigrationsScript;
-DROP PROCEDURE IF EXISTS MigrationsScript;
-DELIMITER //
-CREATE PROCEDURE MigrationsScript()
-BEGIN
- IF NOT EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000000000003_Migration3') THEN
-
- INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
- VALUES ('00000000000003_Migration3', '7.0.0-test');
+COMMIT;
- END IF;
-END //
-DELIMITER ;
-CALL MigrationsScript();
-DROP PROCEDURE MigrationsScript;
-DROP PROCEDURE IF EXISTS MigrationsScript;
-DELIMITER //
-CREATE PROCEDURE MigrationsScript()
-BEGIN
- IF NOT EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000000000004_Migration4') THEN
+""",
+ Sql,
+ ignoreLineEndingDifferences: true);
+ }
- INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
- VALUES ('00000000000004_Migration4', '7.0.0-test');
+ public override async Task Can_generate_idempotent_up_and_down_scripts_noTransactions()
+ {
+ var exception = await Assert.ThrowsAsync(() => base.Can_generate_idempotent_up_and_down_scripts_noTransactions());
- END IF;
-END //
-DELIMITER ;
-CALL MigrationsScript();
-DROP PROCEDURE MigrationsScript;
+ Assert.Equal("'DELIMITER' should not be used with MySqlConnector. See https://mysqlconnector.net/delimiter", exception.Message);
+ Assert.Equal(
+"""
+CREATE TABLE IF NOT EXISTS `__EFMigrationsHistory` (
+ `MigrationId` varchar(150) CHARACTER SET utf8mb4 NOT NULL,
+ `ProductVersion` varchar(32) CHARACTER SET utf8mb4 NOT NULL,
+ CONSTRAINT `PK___EFMigrationsHistory` PRIMARY KEY (`MigrationId`)
+) CHARACTER SET=utf8mb4;
DROP PROCEDURE IF EXISTS MigrationsScript;
DELIMITER //
CREATE PROCEDURE MigrationsScript()
BEGIN
- IF NOT EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000000000005_Migration5') THEN
-
- INSERT INTO Table1 (Id, Bar, Description) VALUES (-1, ' ', 'Value With
+ IF NOT EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000000000001_Migration1') THEN
- Empty Lines')
+ CREATE TABLE `Table1` (
+ `Id` int NOT NULL,
+ `Foo` int NOT NULL,
+ `Description` longtext NOT NULL,
+ CONSTRAINT `PK_Table1` PRIMARY KEY (`Id`)
+ );
END IF;
END //
@@ -751,10 +416,10 @@ INSERT INTO Table1 (Id, Bar, Description) VALUES (-1, ' ', 'Value With
DELIMITER //
CREATE PROCEDURE MigrationsScript()
BEGIN
- IF NOT EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000000000005_Migration5') THEN
+ IF NOT EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000000000001_Migration1') THEN
INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
- VALUES ('00000000000005_Migration5', '7.0.0-test');
+ VALUES ('00000000000001_Migration1', '7.0.0-test');
END IF;
END //
@@ -766,12 +431,9 @@ IF NOT EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000
DELIMITER //
CREATE PROCEDURE MigrationsScript()
BEGIN
- IF NOT EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000000000006_Migration6') THEN
-
- INSERT INTO Table1 (Id, Bar, Description) VALUES (-2, ' ', 'GO
- Value With
+ IF NOT EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000000000002_Migration2') THEN
- Empty Lines')
+ ALTER TABLE `Table1` RENAME COLUMN `Foo` TO `Bar`;
END IF;
END //
@@ -783,30 +445,10 @@ Value With
DELIMITER //
CREATE PROCEDURE MigrationsScript()
BEGIN
- IF NOT EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000000000006_Migration6') THEN
+ IF NOT EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000000000002_Migration2') THEN
INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
- VALUES ('00000000000006_Migration6', '7.0.0-test');
-
- END IF;
-END //
-DELIMITER ;
-CALL MigrationsScript();
-DROP PROCEDURE MigrationsScript;
-
-DROP PROCEDURE IF EXISTS MigrationsScript;
-DELIMITER //
-CREATE PROCEDURE MigrationsScript()
-BEGIN
- IF NOT EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000000000007_Migration7') THEN
-
- INSERT INTO Table1 (Id, Bar, Description) VALUES (-3, ' ', 'GO
- Value With
-
- GO
-
- Empty Lines
- GO')
+ VALUES ('00000000000002_Migration2', '7.0.0-test');
END IF;
END //
@@ -814,26 +456,19 @@ Empty Lines
CALL MigrationsScript();
DROP PROCEDURE MigrationsScript;
-DROP PROCEDURE IF EXISTS MigrationsScript;
-DELIMITER //
-CREATE PROCEDURE MigrationsScript()
-BEGIN
- IF NOT EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000000000007_Migration7') THEN
-
- INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
- VALUES ('00000000000007_Migration7', '7.0.0-test');
-
- END IF;
-END //
-DELIMITER ;
-CALL MigrationsScript();
-DROP PROCEDURE MigrationsScript;
-",
+""",
Sql,
ignoreLineEndingDifferences: true);
}
+ public override void Can_get_active_provider()
+ {
+ base.Can_get_active_provider();
+
+ Assert.Equal("Pomelo.EntityFrameworkCore.MySql", ActiveProvider);
+ }
+
[ConditionalFact(Skip = "TODO: Implement")]
public override void Can_diff_against_2_2_model()
{
@@ -859,19 +494,42 @@ public override void Can_diff_against_2_1_ASP_NET_Identity_model()
}
public override void Can_apply_all_migrations()
- => Assert.Throws(() => base.Can_apply_all_migrations());
+ {
+ base.Can_apply_all_migrations();
+
+ Assert.Null(Sql);
+ }
public override void Can_apply_range_of_migrations()
- => Assert.Throws(() => base.Can_apply_range_of_migrations());
+ {
+ base.Can_apply_range_of_migrations();
+
+ Assert.Null(Sql);
+ }
public override void Can_revert_all_migrations()
- => Assert.Throws(() => base.Can_revert_all_migrations());
+ {
+ base.Can_revert_all_migrations();
+
+ Assert.Null(Sql);
+ }
public override void Can_revert_one_migrations()
- => Assert.Throws(() => base.Can_revert_one_migrations());
+ {
+ base.Can_revert_one_migrations();
+
+ Assert.Null(Sql);
+ }
- public override Task Can_apply_all_migrations_async()
- => Assert.ThrowsAsync(() => base.Can_apply_all_migrations_async());
+ protected override Task ExecuteSqlAsync(string value)
+ => ((MySqlTestStore)Fixture.TestStore).ExecuteNonQueryAsync(value);
+
+ public override async Task Can_apply_all_migrations_async()
+ {
+ await base.Can_apply_all_migrations_async();
+
+ Assert.Null(Sql);
+ }
public class MigrationsInfrastructureMySqlFixture : MigrationsInfrastructureFixtureBase
{
diff --git a/test/EFCore.MySql.FunctionalTests/MigrationsMySqlTest.cs b/test/EFCore.MySql.FunctionalTests/MigrationsMySqlTest.cs
index 462ede9f9..16f52e21a 100644
--- a/test/EFCore.MySql.FunctionalTests/MigrationsMySqlTest.cs
+++ b/test/EFCore.MySql.FunctionalTests/MigrationsMySqlTest.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
@@ -9,8 +9,10 @@
using Microsoft.EntityFrameworkCore.Migrations.Design;
using Microsoft.EntityFrameworkCore.Scaffolding;
using Microsoft.EntityFrameworkCore.Scaffolding.Metadata;
+using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.TestUtilities;
using Microsoft.Extensions.DependencyInjection;
+using MySqlConnector;
using Pomelo.EntityFrameworkCore.MySql.FunctionalTests.TestUtilities;
using Pomelo.EntityFrameworkCore.MySql.Infrastructure;
using Pomelo.EntityFrameworkCore.MySql.Metadata.Internal;
@@ -24,35 +26,73 @@ namespace Pomelo.EntityFrameworkCore.MySql.FunctionalTests
{
public class MigrationsMySqlTest : MigrationsTestBase
{
+ private readonly IRelationalTypeMappingSource _typeMappingSource;
+
public MigrationsMySqlTest(MigrationsMySqlFixture fixture, ITestOutputHelper testOutputHelper)
: base(fixture)
{
Fixture.TestSqlLoggerFactory.Clear();
//Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper);
+
+ _typeMappingSource = Fixture.ServiceProvider.GetService();
}
- [ConditionalTheory(Skip = "TODO: Syntax issue in MySQL 7 only.")]
- public override Task Alter_check_constraint()
+ public override async Task Alter_check_constraint()
{
- return base.Alter_check_constraint();
+ await base.Alter_check_constraint();
+
+ AssertSql(
+"""
+ALTER TABLE `People` DROP CONSTRAINT `CK_People_Foo`;
+""",
+ //
+ """
+ALTER TABLE `People` ADD CONSTRAINT `CK_People_Foo` CHECK (`DriverLicense` > 1);
+""");
}
- [ConditionalTheory(Skip = "TODO")]
- public override Task Alter_column_make_computed(bool? stored)
+ public override async Task Alter_column_make_computed(bool? stored)
{
- return base.Alter_column_make_computed(stored);
+ if (stored == true)
+ {
+ await base.Alter_column_make_computed(stored);
+
+ var computedColumnTypeSql = stored == true ? " STORED" : "";
+
+ AssertSql(
+$"""
+ALTER TABLE `People` MODIFY COLUMN `Sum` int AS (`X` + `Y`){computedColumnTypeSql};
+""");
+ }
+ else
+ {
+ var exception = await Assert.ThrowsAsync(() => base.Alter_column_make_computed(stored));
+ Assert.True(exception.Message is "'Changing the STORED status' is not supported for generated columns."
+ or "This is not yet supported for generated columns");
+ }
}
- [ConditionalTheory(Skip = "TODO")]
- public override Task Add_column_computed_with_collation(bool stored)
+ public override async Task Add_column_computed_with_collation(bool stored)
{
- return base.Add_column_computed_with_collation(stored);
+ await base.Add_column_computed_with_collation(stored);
+
+ var computedColumnTypeSql = stored ? " STORED" : "";
+ var nullableGeneratedColumnSql = AppConfig.ServerVersion.Supports.NullableGeneratedColumns ? " NULL" : string.Empty;
+
+ AssertSql(
+$"""
+ALTER TABLE `People` ADD `Name` longtext CHARACTER SET utf8mb4 COLLATE {NonDefaultCollation} AS ('hello'){computedColumnTypeSql}{nullableGeneratedColumnSql};
+""");
}
- [ConditionalTheory(Skip = "TODO")]
- public override Task Add_column_with_collation()
+ public override async Task Add_column_with_collation()
{
- return base.Add_column_with_collation();
+ await base.Add_column_with_collation();
+
+ AssertSql(
+$"""
+ALTER TABLE `People` ADD `Name` longtext CHARACTER SET utf8mb4 COLLATE {NonDefaultCollation} NULL;
+""");
}
[SupportedServerVersionCondition(nameof(ServerVersionSupport.DefaultExpression), nameof(ServerVersionSupport.AlternativeDefaultExpression))]
@@ -268,52 +308,83 @@ await Test(
});
}
- [ConditionalTheory(Skip = "TODO")]
+ [ConditionalTheory(Skip = "For this to work, either MySqlMigrator needs to be involved, or the primary key related stored procedures need to be handled by MySqlMigrationsSqlGenerator instead. The later is probably the way to go. We should move the primary key related stored procedures to its own service, so ti can be potentially be customized by users.")]
public override async Task Add_primary_key_string()
{
await base.Add_primary_key_string();
+
+ AssertSql("");
}
- [ConditionalTheory(Skip = "TODO")]
- public override Task Add_primary_key_composite_with_name()
+ public override async Task Add_primary_key_composite_with_name()
{
- return base.Add_primary_key_composite_with_name();
+ await base.Add_primary_key_composite_with_name();
+
+ AssertSql(
+"""
+ALTER TABLE `People` ADD CONSTRAINT `PK_Foo` PRIMARY KEY (`SomeField1`, `SomeField2`);
+""");
}
- [ConditionalTheory(Skip = "TODO")]
- public override Task Add_primary_key_with_name()
+ [ConditionalTheory(Skip = "For this to work, either MySqlMigrator needs to be involved, or the primary key related stored procedures need to be handled by MySqlMigrationsSqlGenerator instead. The later is probably the way to go. We should move the primary key related stored procedures to its own service, so ti can be potentially be customized by users.")]
+ public override async Task Add_primary_key_with_name()
{
- return base.Add_primary_key_with_name();
+ await base.Add_primary_key_with_name();
+
+ AssertSql("");
}
- [ConditionalTheory(Skip = "TODO")]
- public override Task Add_unique_constraint()
+ [ConditionalTheory(Skip = "Are we not scaffolding unique constraints yet?")]
+ public override async Task Add_unique_constraint()
{
- return base.Add_unique_constraint();
+ await base.Add_unique_constraint();
+
+ AssertSql("");
}
- [ConditionalTheory(Skip = "TODO")]
- public override Task Add_unique_constraint_composite_with_name()
+ [ConditionalTheory(Skip = "Are we not scaffolding unique constraints yet?")]
+ public override async Task Add_unique_constraint_composite_with_name()
{
- return base.Add_unique_constraint_composite_with_name();
+ await base.Add_unique_constraint_composite_with_name();
+
+ AssertSql("");
}
- [ConditionalTheory(Skip = "TODO")]
- public override Task Alter_column_change_computed_type()
+ public override async Task Alter_column_change_computed_type()
{
- return base.Alter_column_change_computed_type();
+ var exception = await Assert.ThrowsAsync(() => base.Alter_column_change_computed_type());
+ Assert.True(exception.Message is "'Changing the STORED status' is not supported for generated columns."
+ or "This is not yet supported for generated columns");
}
- [ConditionalTheory(Skip = "TODO")]
- public override Task Alter_column_change_type()
+ public override async Task Alter_column_change_type()
{
- return base.Alter_column_change_type();
+ // await base.Alter_column_change_type();
+ await Test(
+ builder => builder.Entity("People").Property("Id"),
+ builder => builder.Entity("People").Property("SomeColumn"),
+ builder => builder.Entity("People").Property("SomeColumn"),
+ model =>
+ {
+ var table = Assert.Single(model.Tables);
+ var column = Assert.Single(table.Columns, c => c.Name == "SomeColumn");
+ Assert.StartsWith(_typeMappingSource.FindMapping(typeof(long)).StoreTypeNameBase, column.StoreType);
+ });
+
+ AssertSql(
+"""
+ALTER TABLE `People` MODIFY COLUMN `SomeColumn` bigint NOT NULL;
+""");
}
- [ConditionalTheory(Skip = "TODO")]
- public override Task Alter_column_set_collation()
+ public override async Task Alter_column_set_collation()
{
- return base.Alter_column_set_collation();
+ await base.Alter_column_set_collation();
+
+ AssertSql(
+$"""
+ALTER TABLE `People` MODIFY COLUMN `Name` longtext CHARACTER SET utf8mb4 COLLATE {NonDefaultCollation} NULL;
+""");
}
[SupportedServerVersionCondition(nameof(ServerVersionSupport.Sequences))]
@@ -322,19 +393,24 @@ public override async Task Alter_sequence_all_settings()
await base.Alter_sequence_all_settings();
AssertSql(
- """
+"""
ALTER SEQUENCE `foo` INCREMENT BY 2 MINVALUE -5 MAXVALUE 10 CYCLE;
""",
//
- """
+"""
ALTER SEQUENCE `foo` START WITH -3 RESTART;
""");
}
[SupportedServerVersionCondition(nameof(ServerVersionSupport.Sequences))]
- public override Task Alter_sequence_increment_by()
+ public override async Task Alter_sequence_increment_by()
{
- return base.Alter_sequence_increment_by();
+ await base.Alter_sequence_increment_by();
+
+ AssertSql(
+"""
+ALTER SEQUENCE `foo` INCREMENT BY 2 NO MINVALUE NO MAXVALUE NOCYCLE;
+""");
}
[SupportedServerVersionCondition(nameof(ServerVersionSupport.Sequences))]
@@ -351,37 +427,63 @@ public override async Task Alter_table_add_comment_non_default_schema()
await base.Alter_table_add_comment_non_default_schema();
AssertSql(
- @"ALTER TABLE `SomeOtherSchema_People` COMMENT 'Table comment';");
+"""
+ALTER TABLE `People` COMMENT 'Table comment';
+""");
}
- [ConditionalTheory(Skip = "TODO")]
- public override Task Create_index_with_filter()
+ [ConditionalFact(Skip = "MySQL does not support filtered indices.")]
+ public override async Task Create_index_with_filter()
{
- return base.Create_index_with_filter();
+ await base.Create_index_with_filter();
+
+ AssertSql("");
}
- [ConditionalTheory(Skip = "TODO")]
- public override Task Create_schema()
+ public override async Task Create_schema()
{
- return base.Create_schema();
+ await base.Create_schema();
+
+ AssertSql(
+"""
+CREATE TABLE `People` (
+ `Id` int NOT NULL AUTO_INCREMENT,
+ CONSTRAINT `PK_People` PRIMARY KEY (`Id`)
+) CHARACTER SET=utf8mb4;
+""");
}
[SupportedServerVersionCondition(nameof(ServerVersionSupport.Sequences))]
- public override Task Create_sequence()
+ public override async Task Create_sequence()
{
- return base.Create_sequence();
+ await base.Create_sequence();
+
+ AssertSql(
+"""
+CREATE SEQUENCE `TestSequence` START WITH 1 INCREMENT BY 1 NOCYCLE;
+""");
}
[SupportedServerVersionCondition(nameof(ServerVersionSupport.Sequences))]
- public override Task Create_sequence_long()
+ public override async Task Create_sequence_long()
{
- return base.Create_sequence_long();
+ await base.Create_sequence_long();
+
+ AssertSql(
+"""
+CREATE SEQUENCE `TestSequence` START WITH 1 INCREMENT BY 1 NOCYCLE;
+""");
}
[SupportedServerVersionCondition(nameof(ServerVersionSupport.Sequences))]
- public override Task Create_sequence_short()
+ public override async Task Create_sequence_short()
{
- return base.Create_sequence_short();
+ await base.Create_sequence_short();
+
+ AssertSql(
+"""
+CREATE SEQUENCE `TestSequence` START WITH 1 INCREMENT BY 1 NOCYCLE;
+""");
}
[SupportedServerVersionCondition(nameof(ServerVersionSupport.Sequences))]
@@ -401,7 +503,7 @@ await Test(
// Assert.Equal("TestSequence", sequence.Name);
// Assert.Equal("dbo2", sequence.Schema);
- Assert.Equal("dbo2_TestSequence", sequence.Name);
+ Assert.Equal("TestSequence", sequence.Name);
Assert.Equal(3, sequence.StartValue);
Assert.Equal(2, sequence.IncrementBy);
@@ -412,14 +514,16 @@ await Test(
AssertSql(
"""
-CREATE SEQUENCE `dbo2_TestSequence` START WITH 3 INCREMENT BY 2 MINVALUE 2 MAXVALUE 916 CYCLE;
+CREATE SEQUENCE `TestSequence` START WITH 3 INCREMENT BY 2 MINVALUE 2 MAXVALUE 916 CYCLE;
""");
}
- [ConditionalTheory(Skip = "TODO")]
- public override Task Create_table_all_settings()
+ [ConditionalFact(Skip = "Are we not scaffolding unique constraints yet?")]
+ public override async Task Create_table_all_settings()
{
- return base.Create_table_all_settings();
+ await base.Create_table_all_settings();
+
+ AssertSql("");
}
public override async Task Create_table_with_multiline_comments()
@@ -440,10 +544,12 @@ More information can
be found in the docs.';");
}
- [ConditionalTheory(Skip = "TODO")]
- public override Task Create_unique_index_with_filter()
+ [ConditionalFact(Skip = "MySQL does not support filtered indices.")]
+ public override async Task Create_unique_index_with_filter()
{
- return base.Create_unique_index_with_filter();
+ await base.Create_unique_index_with_filter();
+
+ AssertSql("");
}
[SupportedServerVersionCondition(nameof(ServerVersionSupport.DescendingIndexes))]
@@ -475,36 +581,52 @@ public override async Task Alter_index_change_sort_order()
@"CREATE INDEX `IX_People_X_Y_Z` ON `People` (`X`, `Y` DESC, `Z`);");
}
- [ConditionalTheory(Skip = "TODO: Syntax issue in MySQL 7 only.")]
- public override Task Drop_check_constraint()
+ public override async Task Drop_check_constraint()
{
- return base.Drop_check_constraint();
+ await base.Drop_check_constraint();
+
+ AssertSql(
+"""
+ALTER TABLE `People` DROP CONSTRAINT `CK_People_Foo`;
+""");
}
- [ConditionalTheory(Skip = "TODO")]
- public override Task Drop_column_primary_key()
+ [ConditionalFact(Skip = "For this to work, either MySqlMigrator needs to be involved, or the primary key related stored procedures need to be handled by MySqlMigrationsSqlGenerator instead. The later is probably the way to go. We should move the primary key related stored procedures to its own service, so ti can be potentially be customized by users.")]
+ public override async Task Drop_column_primary_key()
{
- return base.Drop_column_primary_key();
+ await base.Drop_column_primary_key();
+
+ AssertSql("");
}
- [ConditionalTheory(Skip = "TODO")]
- public override Task Drop_primary_key_int()
+ [ConditionalFact(Skip = "For this to work, either MySqlMigrator needs to be involved, or the primary key related stored procedures need to be handled by MySqlMigrationsSqlGenerator instead. The later is probably the way to go. We should move the primary key related stored procedures to its own service, so ti can be potentially be customized by users.")]
+ public override async Task Drop_primary_key_int()
{
- return base.Drop_primary_key_int();
+ await base.Drop_primary_key_int();
+
+ AssertSql("");
}
- [ConditionalTheory(Skip = "TODO")]
+ [ConditionalFact(Skip = "For this to work, either MySqlMigrator needs to be involved, or the primary key related stored procedures need to be handled by MySqlMigrationsSqlGenerator instead. The later is probably the way to go. We should move the primary key related stored procedures to its own service, so ti can be potentially be customized by users.")]
public override async Task Drop_primary_key_string()
{
await base.Drop_primary_key_string();
+
+ AssertSql("");
}
[SupportedServerVersionCondition(nameof(ServerVersionSupport.Sequences))]
- public override Task Drop_sequence()
+ public override async Task Drop_sequence()
{
- return base.Drop_sequence();
+ await base.Drop_sequence();
+
+ AssertSql(
+"""
+DROP SEQUENCE `TestSequence`;
+""");
}
+ [ConditionalFact(Skip = "There are no schemas in MySQL, that a sequence can be moved between.")]
[SupportedServerVersionCondition(nameof(ServerVersionSupport.Sequences))]
public override async Task Move_sequence()
{
@@ -516,19 +638,45 @@ await Test(
var sequence = Assert.Single(model.Sequences);
// Assert.Equal("TestSequenceSchema", sequence.Schema);
// Assert.Equal("TestSequence", sequence.Name);
- Assert.Equal("TestSequenceSchema_TestSequenceMove", sequence.Name);
+ Assert.Equal("TestSequenceMove", sequence.Name);
});
+ AssertSql("");
+ }
+
+ [SupportedServerVersionCondition(nameof(ServerVersionSupport.DefaultExpression), nameof(ServerVersionSupport.AlternativeDefaultExpression))]
+ public override async Task Add_required_primitive_collection_with_custom_default_value_sql_to_existing_table()
+ {
+ // Classic/literal default values like `DEFAULT '[3, 2, 1]'` are not allowed for `json`, `blob` or `text` data types, but
+ // default *expressions* like `DEFAULT ('[3, 2, 1]')` are.
+ await base.Add_required_primitive_collection_with_custom_default_value_sql_to_existing_table_core("('[3, 2, 1]')");
+
+ AssertSql(
+"""
+ALTER TABLE `Customers` ADD `Numbers` longtext CHARACTER SET utf8mb4 NOT NULL DEFAULT ('[3, 2, 1]');
+""");
+ }
+
+ public override async Task Add_required_primitve_collection_with_custom_default_value_sql_to_existing_table()
+ {
+ // Classic/literal default values like `DEFAULT '[3, 2, 1]'` are not allowed for `json`, `blob` or `text` data types, but
+ // default *expressions* like `DEFAULT ('[3, 2, 1]')` are.
+ await base.Add_required_primitve_collection_with_custom_default_value_sql_to_existing_table_core("('[3, 2, 1]')");
+
AssertSql(
"""
-ALTER TABLE `TestSequenceMove` RENAME `TestSequenceSchema_TestSequenceMove`;
+ALTER TABLE `Customers` ADD `Numbers` longtext CHARACTER SET utf8mb4 NOT NULL DEFAULT ('[3, 2, 1]');
""");
}
- [ConditionalTheory(Skip = "TODO")]
- public override Task Move_table()
+ public override async Task Move_table()
{
- return base.Move_table();
+ await base.Move_table();
+
+ AssertSql(
+"""
+ALTER TABLE `TestTable` RENAME `TestTable`;
+""");
}
[SupportedServerVersionCondition(nameof(ServerVersionSupport.Sequences))]
@@ -565,23 +713,58 @@ await Test(
}
}
- [ConditionalTheory(Skip = "TODO")]
- public override Task Rename_table_with_primary_key()
+ [ConditionalFact(Skip = "For this to work, either MySqlMigrator needs to be involved, or the primary key related stored procedures need to be handled by MySqlMigrationsSqlGenerator instead. The later is probably the way to go. We should move the primary key related stored procedures to its own service, so ti can be potentially be customized by users.")]
+ public override async Task Rename_table_with_primary_key()
{
- return base.Rename_table_with_primary_key();
+ await base.Rename_table_with_primary_key();
+
+ AssertSql("");
}
[SupportedServerVersionCondition(nameof(ServerVersionSupport.GeneratedColumns))]
- public override Task Add_column_with_computedSql(bool? stored)
- => base.Add_column_with_computedSql(stored);
+ public override async Task Add_column_with_computedSql(bool? stored)
+ {
+ await base.Add_column_with_computedSql(stored);
+
+ var computedColumnTypeSql = stored == true ? " STORED" : "";
+ var nullableGeneratedColumnSql = AppConfig.ServerVersion.Supports.NullableGeneratedColumns ? " NULL" : string.Empty;
+
+ AssertSql(
+$"""
+ALTER TABLE `People` ADD `Sum` longtext CHARACTER SET utf8mb4 AS (`X` + `Y`){computedColumnTypeSql}{nullableGeneratedColumnSql};
+""");
+ }
[SupportedServerVersionCondition(nameof(ServerVersionSupport.GeneratedColumns))]
- public override Task Create_table_with_computed_column(bool? stored)
- => base.Create_table_with_computed_column(stored);
+ public override async Task Create_table_with_computed_column(bool? stored)
+ {
+ await base.Create_table_with_computed_column(stored);
+
+ var computedColumnTypeSql = stored == true ? " STORED" : "";
+ var nullableGeneratedColumnSql = AppConfig.ServerVersion.Supports.NullableGeneratedColumns ? " NULL" : string.Empty;
+
+ AssertSql(
+$"""
+CREATE TABLE `People` (
+ `Id` int NOT NULL AUTO_INCREMENT,
+ `Sum` longtext CHARACTER SET utf8mb4 AS (`X` + `Y`){computedColumnTypeSql}{nullableGeneratedColumnSql},
+ `X` int NOT NULL,
+ `Y` int NOT NULL,
+ CONSTRAINT `PK_People` PRIMARY KEY (`Id`)
+) CHARACTER SET=utf8mb4;
+""");
+ }
[SupportedServerVersionCondition(nameof(ServerVersionSupport.GeneratedColumns))]
- public override Task Alter_column_change_computed()
- => base.Alter_column_change_computed();
+ public override async Task Alter_column_change_computed()
+ {
+ await base.Alter_column_change_computed();
+
+ AssertSql(
+"""
+ALTER TABLE `People` MODIFY COLUMN `Sum` int AS (`X` - `Y`);
+""");
+ }
// We currently do not scaffold table options.
//
@@ -911,21 +1094,27 @@ await Test(
}),
result => { });
+ var collationColumnName = AppConfig.ServerVersion.Supports.CollationCharacterSetApplicabilityWithFullCollationNameColumn
+ ? "FULL_COLLATION_NAME"
+ : "COLLATION_NAME";
+
AssertSql(
- @"set @__pomelo_TableCharset = (
- SELECT `ccsa`.`CHARACTER_SET_NAME` as `TABLE_CHARACTER_SET`
- FROM `INFORMATION_SCHEMA`.`TABLES` as `t`
- LEFT JOIN `INFORMATION_SCHEMA`.`COLLATION_CHARACTER_SET_APPLICABILITY` as `ccsa` ON `ccsa`.`COLLATION_NAME` = `t`.`TABLE_COLLATION`
- WHERE `TABLE_SCHEMA` = SCHEMA() AND `TABLE_NAME` = 'IceCream' AND `TABLE_TYPE` IN ('BASE TABLE', 'VIEW'));
+$"""
+set @__pomelo_TableCharset = (
+SELECT `ccsa`.`CHARACTER_SET_NAME` as `TABLE_CHARACTER_SET`
+FROM `INFORMATION_SCHEMA`.`TABLES` as `t`
+LEFT JOIN `INFORMATION_SCHEMA`.`COLLATION_CHARACTER_SET_APPLICABILITY` as `ccsa` ON `ccsa`.`{collationColumnName}` = `t`.`TABLE_COLLATION`
+WHERE `TABLE_SCHEMA` = SCHEMA() AND `TABLE_NAME` = 'IceCream' AND `TABLE_TYPE` IN ('BASE TABLE', 'VIEW'));
SET @__pomelo_SqlExpr = CONCAT('ALTER TABLE `IceCream` CHARACTER SET = ', @__pomelo_TableCharset, ';');
PREPARE __pomelo_SqlExprExecute FROM @__pomelo_SqlExpr;
EXECUTE __pomelo_SqlExprExecute;
-DEALLOCATE PREPARE __pomelo_SqlExprExecute;",
- //
- $@"ALTER TABLE `IceCream` MODIFY COLUMN `Name` longtext COLLATE {NonDefaultCollation} NULL;",
- //
- $@"ALTER TABLE `IceCream` MODIFY COLUMN `Brand` longtext COLLATE {NonDefaultCollation2} NULL;");
+DEALLOCATE PREPARE __pomelo_SqlExprExecute;
+""",
+//
+$@"ALTER TABLE `IceCream` MODIFY COLUMN `Name` longtext COLLATE {NonDefaultCollation} NULL;",
+//
+$@"ALTER TABLE `IceCream` MODIFY COLUMN `Brand` longtext COLLATE {NonDefaultCollation2} NULL;");
}
[ConditionalFact]
@@ -1302,105 +1491,789 @@ public override Task Rename_table()
},
withConventions: false);
- #region ToJson
-
- public override Task Create_table_with_json_column()
- => Assert.ThrowsAsync(() => base.Create_table_with_json_column());
+ public override async Task Create_table()
+ {
+ await base.Create_table();
- public override Task Create_table_with_json_column_explicit_json_column_names()
- => Assert.ThrowsAsync(() => base.Create_table_with_json_column_explicit_json_column_names());
+ AssertSql(
+"""
+CREATE TABLE `People` (
+ `Id` int NOT NULL AUTO_INCREMENT,
+ `Name` longtext CHARACTER SET utf8mb4 NULL,
+ CONSTRAINT `PK_People` PRIMARY KEY (`Id`)
+) CHARACTER SET=utf8mb4;
+""");
+ }
- public override Task Rename_table_with_json_column()
- => Assert.ThrowsAsync(() => base.Rename_table_with_json_column());
+ public override async Task Create_table_no_key()
+ {
+ await base.Create_table_no_key();
- public override Task Add_json_columns_to_existing_table()
- => Assert.ThrowsAsync(() => base.Add_json_columns_to_existing_table());
+ AssertSql(
+"""
+CREATE TABLE `Anonymous` (
+ `SomeColumn` int NOT NULL
+) CHARACTER SET=utf8mb4;
+""");
+ }
- public override Task Convert_json_entities_to_regular_owned()
- => Assert.ThrowsAsync(() => base.Convert_json_entities_to_regular_owned());
+ public override async Task Create_table_with_comments()
+ {
+ await base.Create_table_with_comments();
- public override Task Convert_regular_owned_entities_to_json()
- => Assert.ThrowsAsync(() => base.Convert_regular_owned_entities_to_json());
+ AssertSql(
+"""
+CREATE TABLE `People` (
+ `Id` int NOT NULL AUTO_INCREMENT,
+ `Name` longtext CHARACTER SET utf8mb4 NULL COMMENT 'Column comment',
+ CONSTRAINT `PK_People` PRIMARY KEY (`Id`)
+) CHARACTER SET=utf8mb4 COMMENT='Table comment';
+""");
+ }
- public override Task Convert_string_column_to_a_json_column_containing_reference()
- => Assert.ThrowsAsync(() => base.Convert_string_column_to_a_json_column_containing_reference());
+ public override async Task Alter_table_add_comment()
+ {
+ await base.Alter_table_add_comment();
- public override Task Convert_string_column_to_a_json_column_containing_required_reference()
- => Assert.ThrowsAsync(() => base.Convert_string_column_to_a_json_column_containing_required_reference());
+ AssertSql(
+"""
+ALTER TABLE `People` COMMENT 'Table comment';
+""");
+ }
- public override Task Convert_string_column_to_a_json_column_containing_collection()
- => Assert.ThrowsAsync(() => base.Convert_string_column_to_a_json_column_containing_collection());
+ public override async Task Alter_table_change_comment()
+ {
+ await base.Alter_table_change_comment();
- public override Task Drop_json_columns_from_existing_table()
- => Assert.ThrowsAsync(() => base.Drop_json_columns_from_existing_table());
+ AssertSql(
+"""
+ALTER TABLE `People` COMMENT 'Table comment2';
+""");
+ }
- public override Task Rename_json_column()
- => Assert.ThrowsAsync(() => base.Rename_json_column());
+ public override async Task Alter_table_remove_comment()
+ {
+ await base.Alter_table_remove_comment();
- #endregion ToJson
+ AssertSql(
+"""
+ALTER TABLE `People` COMMENT '';
+""");
+ }
- // The constraint name for a primary key is always PRIMARY in MySQL.
- protected override bool AssertConstraintNames
- => false;
+ public override async Task Drop_table()
+ {
+ await base.Drop_table();
- protected virtual string DefaultCollation => ((MySqlTestStore)Fixture.TestStore).DatabaseCollation;
+ AssertSql(
+"""
+DROP TABLE `People`;
+""");
+ }
- protected override string NonDefaultCollation
- => DefaultCollation == ((MySqlTestStore)Fixture.TestStore).ServerVersion.Value.DefaultUtf8CsCollation
- ? ((MySqlTestStore)Fixture.TestStore).ServerVersion.Value.DefaultUtf8CiCollation
- : ((MySqlTestStore)Fixture.TestStore).ServerVersion.Value.DefaultUtf8CsCollation;
+ public override async Task Add_column_with_defaultValueSql_unspecified()
+ {
+ await base.Add_column_with_defaultValueSql_unspecified();
- protected virtual string NonDefaultCollation2
- => "utf8mb4_german2_ci";
+ AssertSql();
+ }
- protected virtual string DefaultCharSet => ((MySqlTestStore)Fixture.TestStore).DatabaseCharSet;
- protected virtual string NonDefaultCharSet => "latin1";
- protected virtual string NonDefaultCharSet2 => "ascii";
+ public override async Task Add_column_with_defaultValue_unspecified()
+ {
+ await base.Add_column_with_defaultValue_unspecified();
- protected virtual TestHelpers TestHelpers => MySqlTestHelpers.Instance;
+ AssertSql();
+ }
- protected virtual Task Test(
- Action buildCommonAction,
- Action buildSourceAction,
- Action buildTargetAction,
- Action migrationBuilderAction,
- Action asserter,
- bool withConventions = true)
+ public override async Task Add_column_with_computedSql_unspecified()
{
- var services = TestHelpers.CreateContextServices();
- var modelRuntimeInitializer = services.GetRequiredService();
+ await base.Add_column_with_computedSql_unspecified();
- // Build the source model, possibly with conventions
- var sourceModelBuilder = CreateModelBuilder(withConventions);
- buildCommonAction(sourceModelBuilder);
- buildSourceAction(sourceModelBuilder);
- var preSnapshotSourceModel = modelRuntimeInitializer.Initialize(
- (IModel)sourceModelBuilder.Model, designTime: true, validationLogger: null);
+ AssertSql();
+ }
- // Round-trip the source model through a snapshot, compiling it and then extracting it back again.
- // This simulates the real-world migration flow and can expose errors in snapshot generation
- var migrationsCodeGenerator = Fixture.TestHelpers.CreateDesignServiceProvider().GetRequiredService();
- var sourceModelSnapshot = migrationsCodeGenerator.GenerateSnapshot(
- modelSnapshotNamespace: null, typeof(DbContext), "MigrationsTestSnapshot", preSnapshotSourceModel);
- var sourceModel = BuildModelFromSnapshotSource(sourceModelSnapshot);
+ public override async Task Add_column_with_required()
+ {
+ await base.Add_column_with_required();
- // Build the target model, possibly with conventions
- var targetModelBuilder = CreateModelBuilder(withConventions);
- buildCommonAction(targetModelBuilder);
- buildTargetAction(targetModelBuilder);
- var targetModel = modelRuntimeInitializer.Initialize(
- (IModel)targetModelBuilder.Model, designTime: true, validationLogger: null);
+ AssertSql(
+"""
+ALTER TABLE `People` ADD `Name` longtext CHARACTER SET utf8mb4 NOT NULL;
+""");
+ }
- var migrationBuilder = new MigrationBuilder(null);
- migrationBuilderAction(migrationBuilder);
+ public override async Task Add_column_with_ansi()
+ {
+ await base.Add_column_with_ansi();
- return Test(sourceModel, targetModel, migrationBuilder.Operations, asserter);
+ AssertSql(
+"""
+ALTER TABLE `People` ADD `Name` longtext CHARACTER SET utf8mb4 NULL;
+""");
}
- private ModelBuilder CreateModelBuilder(bool withConventions)
- => withConventions ? Fixture.TestHelpers.CreateConventionBuilder() : new ModelBuilder(new ConventionSet());
-
- public class MigrationsMySqlFixture : MigrationsFixtureBase
+ public override async Task Add_column_with_max_length()
+ {
+ await base.Add_column_with_max_length();
+
+ AssertSql(
+"""
+ALTER TABLE `People` ADD `Name` varchar(30) CHARACTER SET utf8mb4 NULL;
+""");
+ }
+
+ public override async Task Add_column_with_unbounded_max_length()
+ {
+ await base.Add_column_with_unbounded_max_length();
+
+ AssertSql(
+"""
+ALTER TABLE `People` ADD `Name` longtext CHARACTER SET utf8mb4 NULL;
+""");
+ }
+
+ public override async Task Add_column_with_max_length_on_derived()
+ {
+ await base.Add_column_with_max_length_on_derived();
+
+ AssertSql();
+ }
+
+ public override async Task Add_column_with_fixed_length()
+ {
+ await base.Add_column_with_fixed_length();
+
+ AssertSql(
+"""
+ALTER TABLE `People` ADD `Name` char(100) CHARACTER SET utf8mb4 NULL;
+""");
+ }
+
+ public override async Task Add_column_with_comment()
+ {
+ await base.Add_column_with_comment();
+
+ AssertSql(
+"""
+ALTER TABLE `People` ADD `FullName` longtext CHARACTER SET utf8mb4 NULL COMMENT 'My comment';
+""");
+ }
+
+ public override async Task Add_column_shared()
+ {
+ await base.Add_column_shared();
+
+ AssertSql();
+ }
+
+ public override async Task Add_column_with_check_constraint()
+ {
+ await base.Add_column_with_check_constraint();
+
+ AssertSql(
+"""
+ALTER TABLE `People` ADD `DriverLicense` int NOT NULL DEFAULT 0;
+""",
+ //
+ """
+ALTER TABLE `People` ADD CONSTRAINT `CK_People_Foo` CHECK (`DriverLicense` > 0);
+""");
+ }
+
+ public override async Task Alter_column_make_required_with_null_data()
+ {
+ await base.Alter_column_make_required_with_null_data();
+
+ AssertSql(
+"""
+UPDATE `People` SET `SomeColumn` = ''
+WHERE `SomeColumn` IS NULL;
+SELECT ROW_COUNT();
+""",
+ //
+ """
+ALTER TABLE `People` MODIFY COLUMN `SomeColumn` longtext CHARACTER SET utf8mb4 NOT NULL;
+""");
+ }
+
+ public override async Task Alter_column_make_required_with_index()
+ {
+ await base.Alter_column_make_required_with_index();
+
+ AssertSql(
+"""
+UPDATE `People` SET `SomeColumn` = ''
+WHERE `SomeColumn` IS NULL;
+SELECT ROW_COUNT();
+""",
+ //
+ """
+ALTER TABLE `People` MODIFY COLUMN `SomeColumn` varchar(255) CHARACTER SET utf8mb4 NOT NULL;
+""");
+ }
+
+ public override async Task Alter_column_make_required_with_composite_index()
+ {
+ await base.Alter_column_make_required_with_composite_index();
+
+ AssertSql(
+"""
+UPDATE `People` SET `FirstName` = ''
+WHERE `FirstName` IS NULL;
+SELECT ROW_COUNT();
+""",
+ //
+ """
+ALTER TABLE `People` MODIFY COLUMN `FirstName` varchar(255) CHARACTER SET utf8mb4 NOT NULL;
+""");
+ }
+
+ public override async Task Alter_column_change_computed_recreates_indexes()
+ {
+ await base.Alter_column_change_computed_recreates_indexes();
+
+ AssertSql(
+"""
+ALTER TABLE `People` MODIFY COLUMN `Sum` int AS (`X` - `Y`);
+""");
+ }
+
+ public override async Task Alter_column_add_comment()
+ {
+ await base.Alter_column_add_comment();
+
+ AssertSql(
+"""
+ALTER TABLE `People` MODIFY COLUMN `Id` int NOT NULL COMMENT 'Some comment' AUTO_INCREMENT;
+""");
+ }
+
+ public override async Task Alter_computed_column_add_comment()
+ {
+ await base.Alter_computed_column_add_comment();
+
+ AssertSql(
+"""
+ALTER TABLE `People` MODIFY COLUMN `SomeColumn` int AS (42) COMMENT 'Some comment';
+""");
+ }
+
+ public override async Task Alter_column_change_comment()
+ {
+ await base.Alter_column_change_comment();
+
+ AssertSql(
+"""
+ALTER TABLE `People` MODIFY COLUMN `Id` int NOT NULL COMMENT 'Some comment2' AUTO_INCREMENT;
+""");
+ }
+
+ public override async Task Alter_column_remove_comment()
+ {
+ await base.Alter_column_remove_comment();
+
+ AssertSql(
+"""
+ALTER TABLE `People` MODIFY COLUMN `Id` int NOT NULL AUTO_INCREMENT;
+""");
+ }
+
+ public override async Task Alter_column_reset_collation()
+ {
+ await base.Alter_column_reset_collation();
+
+ AssertSql(
+"""
+ALTER TABLE `People` MODIFY COLUMN `Name` longtext CHARACTER SET utf8mb4 NULL;
+""");
+ }
+
+ public override async Task Drop_column()
+ {
+ await base.Drop_column();
+
+ AssertSql(
+"""
+ALTER TABLE `People` DROP COLUMN `SomeColumn`;
+""");
+ }
+
+ public override async Task Drop_column_computed_and_non_computed_with_dependency()
+ {
+ await base.Drop_column_computed_and_non_computed_with_dependency();
+
+ AssertSql(
+"""
+ALTER TABLE `People` DROP COLUMN `Y`;
+""",
+ //
+ """
+ALTER TABLE `People` DROP COLUMN `X`;
+""");
+ }
+
+ public override async Task Rename_column()
+ {
+ await base.Rename_column();
+
+ AssertSql(
+"""
+ALTER TABLE `People` RENAME COLUMN `SomeColumn` TO `SomeOtherColumn`;
+""");
+ }
+
+ public override async Task Create_index()
+ {
+ await base.Create_index();
+
+ AssertSql(
+"""
+ALTER TABLE `People` MODIFY COLUMN `FirstName` varchar(255) CHARACTER SET utf8mb4 NULL;
+""",
+ //
+ """
+CREATE INDEX `IX_People_FirstName` ON `People` (`FirstName`);
+""");
+ }
+
+ public override async Task Create_index_unique()
+ {
+ await base.Create_index_unique();
+
+ AssertSql(
+"""
+ALTER TABLE `People` MODIFY COLUMN `LastName` varchar(255) CHARACTER SET utf8mb4 NULL;
+""",
+ //
+ """
+ALTER TABLE `People` MODIFY COLUMN `FirstName` varchar(255) CHARACTER SET utf8mb4 NULL;
+""",
+ //
+ """
+CREATE UNIQUE INDEX `IX_People_FirstName_LastName` ON `People` (`FirstName`, `LastName`);
+""");
+ }
+
+ public override async Task Alter_index_make_unique()
+ {
+ await base.Alter_index_make_unique();
+
+ AssertSql(
+"""
+ALTER TABLE `People` DROP INDEX `IX_People_X`;
+""",
+ //
+ """
+CREATE UNIQUE INDEX `IX_People_X` ON `People` (`X`);
+""");
+ }
+
+ public override async Task Drop_index()
+ {
+ await base.Drop_index();
+
+ AssertSql(
+"""
+ALTER TABLE `People` DROP INDEX `IX_People_SomeField`;
+""");
+ }
+
+ public override async Task Add_primary_key_int()
+ {
+ await base.Add_primary_key_int();
+
+ AssertSql(
+"""
+ALTER TABLE `People` MODIFY COLUMN `SomeField` int NOT NULL AUTO_INCREMENT,
+ADD CONSTRAINT `PK_People` PRIMARY KEY (`SomeField`);
+""");
+ }
+
+ public override async Task Add_foreign_key_with_name()
+ {
+ await base.Add_foreign_key_with_name();
+
+ AssertSql(
+"""
+CREATE INDEX `IX_Orders_CustomerId` ON `Orders` (`CustomerId`);
+""",
+ //
+ """
+ALTER TABLE `Orders` ADD CONSTRAINT `FK_Foo` FOREIGN KEY (`CustomerId`) REFERENCES `Customers` (`Id`) ON DELETE CASCADE;
+""");
+ }
+
+ public override async Task Drop_foreign_key()
+ {
+ await base.Drop_foreign_key();
+
+ AssertSql(
+"""
+ALTER TABLE `Orders` DROP FOREIGN KEY `FK_Orders_Customers_CustomerId`;
+""",
+ //
+ """
+ALTER TABLE `Orders` DROP INDEX `IX_Orders_CustomerId`;
+""");
+ }
+
+ public override async Task Drop_unique_constraint()
+ {
+ await base.Drop_unique_constraint();
+
+ AssertSql(
+"""
+ALTER TABLE `People` DROP KEY `AK_People_AlternateKeyColumn`;
+""");
+ }
+
+ public override async Task Add_check_constraint_with_name()
+ {
+ await base.Add_check_constraint_with_name();
+
+ AssertSql(
+"""
+ALTER TABLE `People` ADD CONSTRAINT `CK_People_Foo` CHECK (`DriverLicense` > 0);
+""");
+ }
+
+ public override async Task InsertDataOperation()
+ {
+ await base.InsertDataOperation();
+
+ AssertSql(
+"""
+INSERT INTO `Person` (`Id`, `Name`)
+VALUES (1, 'Daenerys Targaryen'),
+(2, 'John Snow'),
+(3, 'Arya Stark'),
+(4, 'Harry Strickland'),
+(5, NULL);
+""");
+ }
+
+ public override async Task DeleteDataOperation_simple_key()
+ {
+ await base.DeleteDataOperation_simple_key();
+
+ AssertSql(
+ AppConfig.ServerVersion.Supports.Returning
+ ? """
+DELETE FROM `Person`
+WHERE `Id` = 2
+RETURNING 1;
+"""
+ : """
+DELETE FROM `Person`
+WHERE `Id` = 2;
+SELECT ROW_COUNT();
+""");
+ }
+
+ public override async Task DeleteDataOperation_composite_key()
+ {
+ await base.DeleteDataOperation_composite_key();
+
+ AssertSql(
+ AppConfig.ServerVersion.Supports.Returning
+ ? """
+DELETE FROM `Person`
+WHERE `AnotherId` = 12 AND `Id` = 2
+RETURNING 1;
+"""
+ : """
+DELETE FROM `Person`
+WHERE `AnotherId` = 12 AND `Id` = 2;
+SELECT ROW_COUNT();
+""");
+ }
+
+ public override async Task UpdateDataOperation_simple_key()
+ {
+ await base.UpdateDataOperation_simple_key();
+
+ AssertSql(
+"""
+UPDATE `Person` SET `Name` = 'Another John Snow'
+WHERE `Id` = 2;
+SELECT ROW_COUNT();
+""");
+ }
+
+ public override async Task UpdateDataOperation_composite_key()
+ {
+ await base.UpdateDataOperation_composite_key();
+
+ AssertSql(
+"""
+UPDATE `Person` SET `Name` = 'Another John Snow'
+WHERE `AnotherId` = 11 AND `Id` = 2;
+SELECT ROW_COUNT();
+""");
+ }
+
+ public override async Task UpdateDataOperation_multiple_columns()
+ {
+ await base.UpdateDataOperation_multiple_columns();
+
+ AssertSql(
+"""
+UPDATE `Person` SET `Age` = 21, `Name` = 'Another John Snow'
+WHERE `Id` = 2;
+SELECT ROW_COUNT();
+""");
+ }
+
+ public override async Task SqlOperation()
+ {
+ await base.SqlOperation();
+
+ AssertSql(
+"""
+-- I <3 DDL
+""");
+ }
+
+ public override async Task Create_table_with_complex_type_with_required_properties_on_derived_entity_in_TPH()
+ {
+ await base.Create_table_with_complex_type_with_required_properties_on_derived_entity_in_TPH();
+
+ AssertSql(
+"""
+CREATE TABLE `Contacts` (
+ `Id` int NOT NULL AUTO_INCREMENT,
+ `Discriminator` varchar(8) CHARACTER SET utf8mb4 NOT NULL,
+ `Name` longtext CHARACTER SET utf8mb4 NULL,
+ `Number` int NULL,
+ `MyComplex_Prop` longtext NULL,
+ `MyComplex_MyNestedComplex_Bar` datetime(6) NULL,
+ `MyComplex_MyNestedComplex_Foo` int NULL,
+ CONSTRAINT `PK_Contacts` PRIMARY KEY (`Id`)
+) CHARACTER SET=utf8mb4;
+""");
+ }
+
+ public override async Task Add_required_primitive_collection_to_existing_table()
+ {
+ await base.Add_required_primitive_collection_to_existing_table();
+
+ AssertSql(
+"""
+ALTER TABLE `Customers` ADD `Numbers` longtext CHARACTER SET utf8mb4 NOT NULL;
+""");
+ }
+
+ public override async Task Add_required_primitive_collection_with_custom_default_value_to_existing_table()
+ {
+ await base.Add_required_primitive_collection_with_custom_default_value_to_existing_table();
+
+ AssertSql(
+"""
+ALTER TABLE `Customers` ADD `Numbers` longtext CHARACTER SET utf8mb4 NOT NULL DEFAULT ('[1,2,3]');
+""");
+ }
+
+ public override async Task Add_required_primitive_collection_with_custom_converter_to_existing_table()
+ {
+ await base.Add_required_primitive_collection_with_custom_converter_to_existing_table();
+
+ AssertSql();
+ }
+
+ public override async Task Add_required_primitive_collection_with_custom_converter_and_custom_default_value_to_existing_table()
+ {
+ await base.Add_required_primitive_collection_with_custom_converter_and_custom_default_value_to_existing_table();
+
+ AssertSql(
+"""
+ALTER TABLE `Customers` ADD `Numbers` longtext CHARACTER SET utf8mb4 NOT NULL DEFAULT ('some numbers');
+""");
+ }
+
+ public override async Task Add_optional_primitive_collection_to_existing_table()
+ {
+ await base.Add_optional_primitive_collection_to_existing_table();
+
+ AssertSql(
+"""
+ALTER TABLE `Customers` ADD `Numbers` longtext CHARACTER SET utf8mb4 NULL;
+""");
+ }
+
+ public override async Task Create_table_with_required_primitive_collection()
+ {
+ await base.Create_table_with_required_primitive_collection();
+
+ AssertSql(
+"""
+CREATE TABLE `Customers` (
+ `Id` int NOT NULL AUTO_INCREMENT,
+ `Name` longtext CHARACTER SET utf8mb4 NULL,
+ `Numbers` longtext CHARACTER SET utf8mb4 NOT NULL,
+ CONSTRAINT `PK_Customers` PRIMARY KEY (`Id`)
+) CHARACTER SET=utf8mb4;
+""");
+ }
+
+ public override async Task Create_table_with_optional_primitive_collection()
+ {
+ await base.Create_table_with_optional_primitive_collection();
+
+ AssertSql(
+"""
+CREATE TABLE `Customers` (
+ `Id` int NOT NULL AUTO_INCREMENT,
+ `Name` longtext CHARACTER SET utf8mb4 NULL,
+ `Numbers` longtext CHARACTER SET utf8mb4 NULL,
+ CONSTRAINT `PK_Customers` PRIMARY KEY (`Id`)
+) CHARACTER SET=utf8mb4;
+""");
+ }
+
+ public override async Task Add_required_primitve_collection_to_existing_table()
+ {
+ await base.Add_required_primitve_collection_to_existing_table();
+
+ AssertSql(
+"""
+ALTER TABLE `Customers` ADD `Numbers` longtext CHARACTER SET utf8mb4 NOT NULL;
+""");
+ }
+
+ public override async Task Add_required_primitve_collection_with_custom_default_value_to_existing_table()
+ {
+ await base.Add_required_primitve_collection_with_custom_default_value_to_existing_table();
+
+ AssertSql(
+"""
+ALTER TABLE `Customers` ADD `Numbers` longtext CHARACTER SET utf8mb4 NOT NULL DEFAULT ('[1,2,3]');
+""");
+ }
+
+ public override async Task Add_required_primitve_collection_with_custom_converter_to_existing_table()
+ {
+ await base.Add_required_primitve_collection_with_custom_converter_to_existing_table();
+
+ AssertSql();
+ }
+
+ public override async Task Add_required_primitve_collection_with_custom_converter_and_custom_default_value_to_existing_table()
+ {
+ await base.Add_required_primitve_collection_with_custom_converter_and_custom_default_value_to_existing_table();
+
+ AssertSql(
+"""
+ALTER TABLE `Customers` ADD `Numbers` longtext CHARACTER SET utf8mb4 NOT NULL DEFAULT ('some numbers');
+""");
+ }
+
+ #region ToJson
+
+ public override Task Create_table_with_json_column()
+ => Assert.ThrowsAsync(() => base.Create_table_with_json_column());
+
+ public override Task Create_table_with_json_column_explicit_json_column_names()
+ => Assert.ThrowsAsync(() => base.Create_table_with_json_column_explicit_json_column_names());
+
+ public override Task Rename_table_with_json_column()
+ => Assert.ThrowsAsync(() => base.Rename_table_with_json_column());
+
+ public override Task Add_json_columns_to_existing_table()
+ => Assert.ThrowsAsync(() => base.Add_json_columns_to_existing_table());
+
+ public override Task Convert_json_entities_to_regular_owned()
+ => Assert.ThrowsAsync(() => base.Convert_json_entities_to_regular_owned());
+
+ public override Task Convert_regular_owned_entities_to_json()
+ => Assert.ThrowsAsync(() => base.Convert_regular_owned_entities_to_json());
+
+ public override Task Convert_string_column_to_a_json_column_containing_reference()
+ => Assert.ThrowsAsync(() => base.Convert_string_column_to_a_json_column_containing_reference());
+
+ public override Task Convert_string_column_to_a_json_column_containing_required_reference()
+ => Assert.ThrowsAsync(() => base.Convert_string_column_to_a_json_column_containing_required_reference());
+
+ public override Task Convert_string_column_to_a_json_column_containing_collection()
+ => Assert.ThrowsAsync(() => base.Convert_string_column_to_a_json_column_containing_collection());
+
+ public override Task Drop_json_columns_from_existing_table()
+ => Assert.ThrowsAsync(() => base.Drop_json_columns_from_existing_table());
+
+ public override Task Rename_json_column()
+ => Assert.ThrowsAsync(() => base.Rename_json_column());
+
+ #endregion ToJson
+
+ [ConditionalFact]
+ public virtual void Check_all_tests_overridden()
+ => MySqlTestHelpers.AssertAllMethodsOverridden(GetType());
+
+ // The constraint name for a primary key is always PRIMARY in MySQL.
+ protected override bool AssertConstraintNames
+ => false;
+
+ // MySQL does not support the concept of schemas.
+ protected override bool AssertSchemaNames
+ => false;
+
+ protected virtual string DefaultCollation => ((MySqlTestStore)Fixture.TestStore).DatabaseCollation;
+
+ protected override string NonDefaultCollation
+ => DefaultCollation == ((MySqlTestStore)Fixture.TestStore).ServerVersion.Value.DefaultUtf8CsCollation
+ ? ((MySqlTestStore)Fixture.TestStore).ServerVersion.Value.DefaultUtf8CiCollation
+ : ((MySqlTestStore)Fixture.TestStore).ServerVersion.Value.DefaultUtf8CsCollation;
+
+ protected virtual string NonDefaultCollation2
+ => "utf8mb4_german2_ci";
+
+ protected virtual string DefaultCharSet => ((MySqlTestStore)Fixture.TestStore).DatabaseCharSet;
+ protected virtual string NonDefaultCharSet => "latin1";
+ protected virtual string NonDefaultCharSet2 => "ascii";
+
+ protected virtual TestHelpers TestHelpers => MySqlTestHelpers.Instance;
+
+ protected virtual Task Test(
+ Action buildCommonAction,
+ Action buildSourceAction,
+ Action buildTargetAction,
+ Action migrationBuilderAction,
+ Action asserter,
+ bool withConventions = true)
+ {
+ var services = TestHelpers.CreateContextServices();
+ var modelRuntimeInitializer = services.GetRequiredService();
+
+ // Build the source model, possibly with conventions
+ var sourceModelBuilder = CreateModelBuilder(withConventions);
+ buildCommonAction(sourceModelBuilder);
+ buildSourceAction(sourceModelBuilder);
+ var preSnapshotSourceModel = modelRuntimeInitializer.Initialize(
+ (IModel)sourceModelBuilder.Model, designTime: true, validationLogger: null);
+
+ // Round-trip the source model through a snapshot, compiling it and then extracting it back again.
+ // This simulates the real-world migration flow and can expose errors in snapshot generation
+ var migrationsCodeGenerator = Fixture.TestHelpers.CreateDesignServiceProvider().GetRequiredService();
+ var sourceModelSnapshot = migrationsCodeGenerator.GenerateSnapshot(
+ modelSnapshotNamespace: null, typeof(DbContext), "MigrationsTestSnapshot", preSnapshotSourceModel);
+ var sourceModel = BuildModelFromSnapshotSource(sourceModelSnapshot);
+
+ // Build the target model, possibly with conventions
+ var targetModelBuilder = CreateModelBuilder(withConventions);
+ buildCommonAction(targetModelBuilder);
+ buildTargetAction(targetModelBuilder);
+ var targetModel = modelRuntimeInitializer.Initialize(
+ (IModel)targetModelBuilder.Model, designTime: true, validationLogger: null);
+
+ var migrationBuilder = new MigrationBuilder(null);
+ migrationBuilderAction(migrationBuilder);
+
+ return Test(sourceModel, targetModel, migrationBuilder.Operations, asserter);
+ }
+
+ private ModelBuilder CreateModelBuilder(bool withConventions)
+ => withConventions ? Fixture.TestHelpers.CreateConventionBuilder() : new ModelBuilder(new ConventionSet());
+
+ public class MigrationsMySqlFixture : MigrationsFixtureBase
{
protected override string StoreName
=> nameof(MigrationsMySqlTest);
@@ -1411,8 +2284,7 @@ protected override string StoreName
public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder)
{
new MySqlDbContextOptionsBuilder(builder)
- .SchemaBehavior(MySqlSchemaBehavior.Translate, (schema, table) => $"{schema}_{table}");
-
+ .SchemaBehavior(MySqlSchemaBehavior.Ignore);
return base.AddOptions(builder);
}
diff --git a/test/EFCore.MySql.FunctionalTests/MySqlComplianceTest.cs b/test/EFCore.MySql.FunctionalTests/MySqlComplianceTest.cs
index 65753ad2a..dac7c8a99 100644
--- a/test/EFCore.MySql.FunctionalTests/MySqlComplianceTest.cs
+++ b/test/EFCore.MySql.FunctionalTests/MySqlComplianceTest.cs
@@ -17,6 +17,13 @@ public class MySqlComplianceTest : RelationalComplianceTestBase
typeof(CommandInterceptionTestBase),
typeof(NorthwindQueryTaggingQueryTestBase<>),
+ // TODO: 9.0
+ typeof(AdHocComplexTypeQueryTestBase),
+ typeof(AdHocPrecompiledQueryRelationalTestBase),
+ typeof(JsonQueryRelationalTestBase<>),
+ typeof(PrecompiledQueryRelationalTestBase),
+ typeof(PrecompiledSqlPregenerationQueryRelationalTestBase),
+
// TODO: Reenable LoggingMySqlTest once its issue has been fixed in EF Core upstream.
typeof(LoggingTestBase),
typeof(LoggingRelationalTestBase<,>),
diff --git a/test/EFCore.MySql.FunctionalTests/MySqlMigrationsSqlGeneratorTest.cs b/test/EFCore.MySql.FunctionalTests/MySqlMigrationsSqlGeneratorTest.cs
index c7a97e22c..6d7680612 100644
--- a/test/EFCore.MySql.FunctionalTests/MySqlMigrationsSqlGeneratorTest.cs
+++ b/test/EFCore.MySql.FunctionalTests/MySqlMigrationsSqlGeneratorTest.cs
@@ -1380,17 +1380,23 @@ public virtual void AlterTableOperation_with_collation_reset()
.OldAnnotation(RelationalAnnotationNames.Collation, "latin1_general_ci");
});
+ var collationColumnName = AppConfig.ServerVersion.Supports.CollationCharacterSetApplicabilityWithFullCollationNameColumn
+ ? "FULL_COLLATION_NAME"
+ : "COLLATION_NAME";
+
Assert.Equal(
- @"set @__pomelo_TableCharset = (
- SELECT `ccsa`.`CHARACTER_SET_NAME` as `TABLE_CHARACTER_SET`
- FROM `INFORMATION_SCHEMA`.`TABLES` as `t`
- LEFT JOIN `INFORMATION_SCHEMA`.`COLLATION_CHARACTER_SET_APPLICABILITY` as `ccsa` ON `ccsa`.`COLLATION_NAME` = `t`.`TABLE_COLLATION`
- WHERE `TABLE_SCHEMA` = SCHEMA() AND `TABLE_NAME` = 'IceCreams' AND `TABLE_TYPE` IN ('BASE TABLE', 'VIEW'));
+$"""
+set @__pomelo_TableCharset = (
+SELECT `ccsa`.`CHARACTER_SET_NAME` as `TABLE_CHARACTER_SET`
+FROM `INFORMATION_SCHEMA`.`TABLES` as `t`
+LEFT JOIN `INFORMATION_SCHEMA`.`COLLATION_CHARACTER_SET_APPLICABILITY` as `ccsa` ON `ccsa`.`{collationColumnName}` = `t`.`TABLE_COLLATION`
+WHERE `TABLE_SCHEMA` = SCHEMA() AND `TABLE_NAME` = 'IceCreams' AND `TABLE_TYPE` IN ('BASE TABLE', 'VIEW'));
SET @__pomelo_SqlExpr = CONCAT('ALTER TABLE `IceCreams` CHARACTER SET = ', @__pomelo_TableCharset, ';');
PREPARE __pomelo_SqlExprExecute FROM @__pomelo_SqlExpr;
EXECUTE __pomelo_SqlExprExecute;
-DEALLOCATE PREPARE __pomelo_SqlExprExecute;" + EOL,
+DEALLOCATE PREPARE __pomelo_SqlExprExecute;
+""" + EOL,
Sql,
ignoreLineEndingDifferences: true);
}
@@ -1499,17 +1505,23 @@ public virtual void AlterTableOperation_with_charset_reset()
.OldAnnotation(MySqlAnnotationNames.CharSet, "latin1");
});
+ var collationColumnName = AppConfig.ServerVersion.Supports.CollationCharacterSetApplicabilityWithFullCollationNameColumn
+ ? "FULL_COLLATION_NAME"
+ : "COLLATION_NAME";
+
Assert.Equal(
- @"set @__pomelo_TableCharset = (
- SELECT `ccsa`.`CHARACTER_SET_NAME` as `TABLE_CHARACTER_SET`
- FROM `INFORMATION_SCHEMA`.`TABLES` as `t`
- LEFT JOIN `INFORMATION_SCHEMA`.`COLLATION_CHARACTER_SET_APPLICABILITY` as `ccsa` ON `ccsa`.`COLLATION_NAME` = `t`.`TABLE_COLLATION`
- WHERE `TABLE_SCHEMA` = SCHEMA() AND `TABLE_NAME` = 'IceCreams' AND `TABLE_TYPE` IN ('BASE TABLE', 'VIEW'));
+$"""
+set @__pomelo_TableCharset = (
+SELECT `ccsa`.`CHARACTER_SET_NAME` as `TABLE_CHARACTER_SET`
+FROM `INFORMATION_SCHEMA`.`TABLES` as `t`
+LEFT JOIN `INFORMATION_SCHEMA`.`COLLATION_CHARACTER_SET_APPLICABILITY` as `ccsa` ON `ccsa`.`{collationColumnName}` = `t`.`TABLE_COLLATION`
+WHERE `TABLE_SCHEMA` = SCHEMA() AND `TABLE_NAME` = 'IceCreams' AND `TABLE_TYPE` IN ('BASE TABLE', 'VIEW'));
SET @__pomelo_SqlExpr = CONCAT('ALTER TABLE `IceCreams` CHARACTER SET = ', @__pomelo_TableCharset, ';');
PREPARE __pomelo_SqlExprExecute FROM @__pomelo_SqlExpr;
EXECUTE __pomelo_SqlExprExecute;
-DEALLOCATE PREPARE __pomelo_SqlExprExecute;" + EOL,
+DEALLOCATE PREPARE __pomelo_SqlExprExecute;
+""" + EOL,
Sql,
ignoreLineEndingDifferences: true);
}
@@ -1696,7 +1708,6 @@ public virtual void CreateSequenceOperation_with_minValue_and_maxValue()
[SupportedServerVersionCondition(nameof(ServerVersionSupport.Sequences))]
public virtual void CreateSequenceOperation_without_minValue_and_maxValue()
{
-
Generate(
new CreateSequenceOperation
{
@@ -1707,7 +1718,7 @@ public virtual void CreateSequenceOperation_without_minValue_and_maxValue()
});
Assert.Equal(
- @"CREATE SEQUENCE `MySequence` START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE NOCYCLE;" + EOL,
+ @"CREATE SEQUENCE `MySequence` START WITH 1 INCREMENT BY 1 NOCYCLE;" + EOL,
Sql,
ignoreLineEndingDifferences: true);
}
diff --git a/test/EFCore.MySql.FunctionalTests/OptimisticConcurrencyMySqlTest.cs b/test/EFCore.MySql.FunctionalTests/OptimisticConcurrencyMySqlTest.cs
index ee81309c2..a32a39a0e 100644
--- a/test/EFCore.MySql.FunctionalTests/OptimisticConcurrencyMySqlTest.cs
+++ b/test/EFCore.MySql.FunctionalTests/OptimisticConcurrencyMySqlTest.cs
@@ -1,9 +1,7 @@
-using System;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage;
-using Microsoft.EntityFrameworkCore.TestModels.ConcurrencyModel;
using Xunit;
namespace Pomelo.EntityFrameworkCore.MySql.FunctionalTests
@@ -18,19 +16,6 @@ public OptimisticConcurrencyMySqlTest(F1MySqlFixture fixture)
protected override void UseTransaction(DatabaseFacade facade, IDbContextTransaction transaction)
=> facade.UseTransaction(transaction.GetDbTransaction());
- protected override Task ConcurrencyTestAsync(
- Action storeChange, Action clientChange,
- Action resolver, Action validator)
- {
- return base.ConcurrencyTestAsync(c =>
- {
- storeChange(c);
- // CHECK: Is this still/really needed?
- // Need to wait to make CURRENT_TIMESTAMP return different values reliably
- Task.Delay(100);
- }, clientChange, resolver, validator);
- }
-
[ConditionalFact(Skip = "#588")]
public override Task Updating_then_deleting_the_same_entity_results_in_DbUpdateConcurrencyException_which_can_be_resolved_with_store_values()
{
diff --git a/test/EFCore.MySql.FunctionalTests/ProxyGraphUpdatesMySqlTest.cs b/test/EFCore.MySql.FunctionalTests/ProxyGraphUpdatesMySqlTest.cs
index 31c13a57e..37fd67a06 100644
--- a/test/EFCore.MySql.FunctionalTests/ProxyGraphUpdatesMySqlTest.cs
+++ b/test/EFCore.MySql.FunctionalTests/ProxyGraphUpdatesMySqlTest.cs
@@ -1,4 +1,5 @@
-using Microsoft.EntityFrameworkCore;
+using System.Threading.Tasks;
+using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.TestUtilities;
@@ -63,9 +64,8 @@ public ChangeTracking(ProxyGraphUpdatesWithChangeTrackingMySqlFixture fixture)
}
// Needs lazy loading
- public override void Save_two_entity_cycle_with_lazy_loading()
- {
- }
+ public override Task Save_two_entity_cycle_with_lazy_loading()
+ => Task.CompletedTask;
protected override bool DoesLazyLoading
=> false;
diff --git a/test/EFCore.MySql.FunctionalTests/Query/AdHocJsonQueryMySqlTest.cs b/test/EFCore.MySql.FunctionalTests/Query/AdHocJsonQueryMySqlTest.cs
index fd8349a93..a9a14d8af 100644
--- a/test/EFCore.MySql.FunctionalTests/Query/AdHocJsonQueryMySqlTest.cs
+++ b/test/EFCore.MySql.FunctionalTests/Query/AdHocJsonQueryMySqlTest.cs
@@ -1,9 +1,14 @@
using System;
+using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Diagnostics;
+using Microsoft.EntityFrameworkCore.Diagnostics.Internal;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.TestUtilities;
+using MySqlConnector;
+using Pomelo.EntityFrameworkCore.MySql.Diagnostics.Internal;
using Pomelo.EntityFrameworkCore.MySql.FunctionalTests.TestUtilities;
using Xunit;
@@ -15,7 +20,7 @@ internal class AdHocJsonQueryMySqlTest : AdHocJsonQueryTestBase
protected override ITestStoreFactory TestStoreFactory
=> MySqlTestStoreFactory.Instance;
- protected override void Seed29219(MyContext29219 ctx)
+ protected override async Task Seed29219(DbContext ctx)
{
var entity1 = new MyEntity29219
{
@@ -23,9 +28,9 @@ protected override void Seed29219(MyContext29219 ctx)
Reference = new MyJsonEntity29219 { NonNullableScalar = 10, NullableScalar = 11 },
Collection =
[
- new() { NonNullableScalar = 100, NullableScalar = 101 },
- new() { NonNullableScalar = 200, NullableScalar = 201 },
- new() { NonNullableScalar = 300, NullableScalar = null }
+ new MyJsonEntity29219 { NonNullableScalar = 100, NullableScalar = 101 },
+ new MyJsonEntity29219 { NonNullableScalar = 200, NullableScalar = 201 },
+ new MyJsonEntity29219 { NonNullableScalar = 300, NullableScalar = null }
]
};
@@ -33,59 +38,66 @@ protected override void Seed29219(MyContext29219 ctx)
{
Id = 2,
Reference = new MyJsonEntity29219 { NonNullableScalar = 20, NullableScalar = null },
- Collection = [new() { NonNullableScalar = 1001, NullableScalar = null }]
+ Collection = [new MyJsonEntity29219 { NonNullableScalar = 1001, NullableScalar = null }]
};
- ctx.Entities.AddRange(entity1, entity2);
- ctx.SaveChanges();
+ ctx.AddRange(entity1, entity2);
+ await ctx.SaveChangesAsync();
- ctx.Database.ExecuteSqlRaw(
- """
+ await ctx.Database.ExecuteSqlAsync(
+ $$"""
INSERT INTO `Entities` (`Id`, `Reference`, `Collection`)
-VALUES(3, '{{ "NonNullableScalar" : 30 }}', '[{{ "NonNullableScalar" : 10001 }}]')
+VALUES(3, N'{ "NonNullableScalar" : 30 }', N'[{ "NonNullableScalar" : 10001 }]')
""");
}
- protected override void Seed30028(MyContext30028 ctx)
+ protected override async Task Seed30028(DbContext ctx)
{
// complete
- ctx.Database.ExecuteSqlRaw(
- """
+ await ctx.Database.ExecuteSqlAsync(
+ $$$$"""
INSERT INTO `Entities` (`Id`, `Json`)
VALUES(
1,
-'{{"RootName":"e1","Collection":[{{"BranchName":"e1 c1","Nested":{{"LeafName":"e1 c1 l"}}}},{{"BranchName":"e1 c2","Nested":{{"LeafName":"e1 c2 l"}}}}],"OptionalReference":{{"BranchName":"e1 or","Nested":{{"LeafName":"e1 or l"}}}},"RequiredReference":{{"BranchName":"e1 rr","Nested":{{"LeafName":"e1 rr l"}}}}}}')
+N'{"RootName":"e1","Collection":[{"BranchName":"e1 c1","Nested":{"LeafName":"e1 c1 l"}},{"BranchName":"e1 c2","Nested":{"LeafName":"e1 c2 l"}}],"OptionalReference":{"BranchName":"e1 or","Nested":{"LeafName":"e1 or l"}},"RequiredReference":{"BranchName":"e1 rr","Nested":{"LeafName":"e1 rr l"}}}')
""");
// missing collection
- ctx.Database.ExecuteSqlRaw(
- """
+ await ctx.Database.ExecuteSqlAsync(
+ $$$$"""
INSERT INTO `Entities` (`Id`, `Json`)
VALUES(
2,
-'{{"RootName":"e2","OptionalReference":{{"BranchName":"e2 or","Nested":{{"LeafName":"e2 or l"}}}},"RequiredReference":{{"BranchName":"e2 rr","Nested":{{"LeafName":"e2 rr l"}}}}}}')
+N'{"RootName":"e2","OptionalReference":{"BranchName":"e2 or","Nested":{"LeafName":"e2 or l"}},"RequiredReference":{"BranchName":"e2 rr","Nested":{"LeafName":"e2 rr l"}}}')
""");
// missing optional reference
- ctx.Database.ExecuteSqlRaw(
- """
+ await ctx.Database.ExecuteSqlAsync(
+ $$$$"""
INSERT INTO `Entities` (`Id`, `Json`)
VALUES(
3,
-'{{"RootName":"e3","Collection":[{{"BranchName":"e3 c1","Nested":{{"LeafName":"e3 c1 l"}}}},{{"BranchName":"e3 c2","Nested":{{"LeafName":"e3 c2 l"}}}}],"RequiredReference":{{"BranchName":"e3 rr","Nested":{{"LeafName":"e3 rr l"}}}}}}')
+N'{"RootName":"e3","Collection":[{"BranchName":"e3 c1","Nested":{"LeafName":"e3 c1 l"}},{"BranchName":"e3 c2","Nested":{"LeafName":"e3 c2 l"}}],"RequiredReference":{"BranchName":"e3 rr","Nested":{"LeafName":"e3 rr l"}}}')
""");
// missing required reference
- ctx.Database.ExecuteSqlRaw(
- """
+ await ctx.Database.ExecuteSqlAsync(
+ $$$$"""
INSERT INTO `Entities` (`Id`, `Json`)
VALUES(
4,
-'{{"RootName":"e4","Collection":[{{"BranchName":"e4 c1","Nested":{{"LeafName":"e4 c1 l"}}}},{{"BranchName":"e4 c2","Nested":{{"LeafName":"e4 c2 l"}}}}],"OptionalReference":{{"BranchName":"e4 or","Nested":{{"LeafName":"e4 or l"}}}}}}')
+N'{"RootName":"e4","Collection":[{"BranchName":"e4 c1","Nested":{"LeafName":"e4 c1 l"}},{"BranchName":"e4 c2","Nested":{"LeafName":"e4 c2 l"}}],"OptionalReference":{"BranchName":"e4 or","Nested":{"LeafName":"e4 or l"}}}')
""");
}
- protected override void SeedArrayOfPrimitives(MyContextArrayOfPrimitives ctx)
+ protected override Task Seed33046(DbContext ctx)
+ => ctx.Database.ExecuteSqlAsync(
+ $$"""
+INSERT INTO `Reviews` (`Rounds`, `Id`)
+VALUES(N'[{"RoundNumber":11,"SubRounds":[{"SubRoundNumber":111},{"SubRoundNumber":112}]}]', 1)
+""");
+
+ protected override Task SeedArrayOfPrimitives(DbContext ctx)
{
var entity1 = new MyEntityArrayOfPrimitives
{
@@ -102,8 +114,8 @@ protected override void SeedArrayOfPrimitives(MyContextArrayOfPrimitives ctx)
},
Collection =
[
- new() { IntArray = [111, 112, 113], ListOfString = ["Foo11", "Bar11"] },
- new() { IntArray = [211, 212, 213], ListOfString = ["Foo12", "Bar12"] }
+ new MyJsonEntityArrayOfPrimitives { IntArray = [111, 112, 113], ListOfString = ["Foo11", "Bar11"] },
+ new MyJsonEntityArrayOfPrimitives { IntArray = [211, 212, 213], ListOfString = ["Foo12", "Bar12"] }
]
};
@@ -122,151 +134,263 @@ protected override void SeedArrayOfPrimitives(MyContextArrayOfPrimitives ctx)
},
Collection =
[
- new() { IntArray = [110, 120, 130], ListOfString = ["A1", "Z1"] },
- new() { IntArray = [210, 220, 230], ListOfString = ["A2", "Z2"] }
+ new MyJsonEntityArrayOfPrimitives { IntArray = [110, 120, 130], ListOfString = ["A1", "Z1"] },
+ new MyJsonEntityArrayOfPrimitives { IntArray = [210, 220, 230], ListOfString = ["A2", "Z2"] }
]
};
- ctx.Entities.AddRange(entity1, entity2);
- ctx.SaveChanges();
+ ctx.AddRange(entity1, entity2);
+ return ctx.SaveChangesAsync();
}
- protected override void SeedJunkInJson(MyContextJunkInJson ctx)
- => ctx.Database.ExecuteSqlRaw(
- """
+ protected override Task SeedJunkInJson(DbContext ctx)
+ => ctx.Database.ExecuteSqlAsync(
+ $$$$"""
INSERT INTO `Entities` (`Collection`, `CollectionWithCtor`, `Reference`, `ReferenceWithCtor`, `Id`)
VALUES(
-'[{{"JunkReference":{{"Something":"SomeValue" }},"Name":"c11","JunkProperty1":50,"Number":11.5,"JunkCollection1":[],"JunkCollection2":[{{"Foo":"junk value"}}],"NestedCollection":[{{"DoB":"2002-04-01T00:00:00","DummyProp":"Dummy value"}},{{"DoB":"2002-04-02T00:00:00","DummyReference":{{"Foo":5}}}}],"NestedReference":{{"DoB":"2002-03-01T00:00:00"}}}},{{"Name":"c12","Number":12.5,"NestedCollection":[{{"DoB":"2002-06-01T00:00:00"}},{{"DoB":"2002-06-02T00:00:00"}}],"NestedDummy":59,"NestedReference":{{"DoB":"2002-05-01T00:00:00"}}}}]',
-'[{{"MyBool":true,"Name":"c11 ctor","JunkReference":{{"Something":"SomeValue","JunkCollection":[{{"Foo":"junk value"}}]}},"NestedCollection":[{{"DoB":"2002-08-01T00:00:00"}},{{"DoB":"2002-08-02T00:00:00"}}],"NestedReference":{{"DoB":"2002-07-01T00:00:00"}}}},{{"MyBool":false,"Name":"c12 ctor","NestedCollection":[{{"DoB":"2002-10-01T00:00:00"}},{{"DoB":"2002-10-02T00:00:00"}}],"JunkCollection":[{{"Foo":"junk value"}}],"NestedReference":{{"DoB":"2002-09-01T00:00:00"}}}}]',
-'{{"Name":"r1","JunkCollection":[{{"Foo":"junk value"}}],"JunkReference":{{"Something":"SomeValue" }},"Number":1.5,"NestedCollection":[{{"DoB":"2000-02-01T00:00:00","JunkReference":{{"Something":"SomeValue"}}}},{{"DoB":"2000-02-02T00:00:00"}}],"NestedReference":{{"DoB":"2000-01-01T00:00:00"}}}}',
-'{{"MyBool":true,"JunkCollection":[{{"Foo":"junk value"}}],"Name":"r1 ctor","JunkReference":{{"Something":"SomeValue" }},"NestedCollection":[{{"DoB":"2001-02-01T00:00:00"}},{{"DoB":"2001-02-02T00:00:00"}}],"NestedReference":{{"JunkCollection":[{{"Foo":"junk value"}}],"DoB":"2001-01-01T00:00:00"}}}}',
+N'[{"JunkReference":{"Something":"SomeValue" },"Name":"c11","JunkProperty1":50,"Number":11.5,"JunkCollection1":[],"JunkCollection2":[{"Foo":"junk value"}],"NestedCollection":[{"DoB":"2002-04-01T00:00:00","DummyProp":"Dummy value"},{"DoB":"2002-04-02T00:00:00","DummyReference":{"Foo":5}}],"NestedReference":{"DoB":"2002-03-01T00:00:00"}},{"Name":"c12","Number":12.5,"NestedCollection":[{"DoB":"2002-06-01T00:00:00"},{"DoB":"2002-06-02T00:00:00"}],"NestedDummy":59,"NestedReference":{"DoB":"2002-05-01T00:00:00"}}]',
+N'[{"MyBool":true,"Name":"c11 ctor","JunkReference":{"Something":"SomeValue","JunkCollection":[{"Foo":"junk value"}]},"NestedCollection":[{"DoB":"2002-08-01T00:00:00"},{"DoB":"2002-08-02T00:00:00"}],"NestedReference":{"DoB":"2002-07-01T00:00:00"}},{"MyBool":false,"Name":"c12 ctor","NestedCollection":[{"DoB":"2002-10-01T00:00:00"},{"DoB":"2002-10-02T00:00:00"}],"JunkCollection":[{"Foo":"junk value"}],"NestedReference":{"DoB":"2002-09-01T00:00:00"}}]',
+N'{"Name":"r1","JunkCollection":[{"Foo":"junk value"}],"JunkReference":{"Something":"SomeValue" },"Number":1.5,"NestedCollection":[{"DoB":"2000-02-01T00:00:00","JunkReference":{"Something":"SomeValue"}},{"DoB":"2000-02-02T00:00:00"}],"NestedReference":{"DoB":"2000-01-01T00:00:00"}}',
+N'{"MyBool":true,"JunkCollection":[{"Foo":"junk value"}],"Name":"r1 ctor","JunkReference":{"Something":"SomeValue" },"NestedCollection":[{"DoB":"2001-02-01T00:00:00"},{"DoB":"2001-02-02T00:00:00"}],"NestedReference":{"JunkCollection":[{"Foo":"junk value"}],"DoB":"2001-01-01T00:00:00"}}',
1)
""");
- protected override void SeedTrickyBuffering(MyContextTrickyBuffering ctx)
- => ctx.Database.ExecuteSqlRaw(
- """
+ protected override Task SeedTrickyBuffering(DbContext ctx)
+ => ctx.Database.ExecuteSqlAsync(
+ $$$"""
INSERT INTO `Entities` (`Reference`, `Id`)
VALUES(
-'{{"Name": "r1", "Number": 7, "JunkReference":{{"Something": "SomeValue" }}, "JunkCollection": [{{"Foo": "junk value"}}], "NestedReference": {{"DoB": "2000-01-01T00:00:00Z"}}, "NestedCollection": [{{"DoB": "2000-02-01T00:00:00Z", "JunkReference": {{"Something": "SomeValue"}}}}, {{"DoB": "2000-02-02T00:00:00Z"}}]}}',1)
+N'{"Name": "r1", "Number": 7, "JunkReference":{"Something": "SomeValue" }, "JunkCollection": [{"Foo": "junk value"}], "NestedReference": {"DoB": "2000-01-01T00:00:00"}, "NestedCollection": [{"DoB": "2000-02-01T00:00:00", "JunkReference": {"Something": "SomeValue"}}, {"DoB": "2000-02-02T00:00:00"}]}',1)
""");
- protected override void SeedShadowProperties(MyContextShadowProperties ctx)
- => ctx.Database.ExecuteSqlRaw(
- """
+ protected override Task SeedShadowProperties(DbContext ctx)
+ => ctx.Database.ExecuteSqlAsync(
+ $$"""
INSERT INTO `Entities` (`Collection`, `CollectionWithCtor`, `Reference`, `ReferenceWithCtor`, `Id`, `Name`)
VALUES(
-'[{{"Name":"e1_c1","ShadowDouble":5.5}},{{"ShadowDouble":20.5,"Name":"e1_c2"}}]',
-'[{{"Name":"e1_c1 ctor","ShadowNullableByte":6}},{{"ShadowNullableByte":null,"Name":"e1_c2 ctor"}}]',
-'{{"Name":"e1_r", "ShadowString":"Foo"}}',
-'{{"ShadowInt":143,"Name":"e1_r ctor"}}',
+N'[{"Name":"e1_c1","ShadowDouble":5.5},{"ShadowDouble":20.5,"Name":"e1_c2"}]',
+N'[{"Name":"e1_c1 ctor","ShadowNullableByte":6},{"ShadowNullableByte":null,"Name":"e1_c2 ctor"}]',
+N'{"Name":"e1_r", "ShadowString":"Foo"}',
+N'{"ShadowInt":143,"Name":"e1_r ctor"}',
1,
-'e1')
+N'e1')
""");
- protected override void SeedNotICollection(MyContextNotICollection ctx)
+ protected override async Task SeedNotICollection(DbContext ctx)
{
- ctx.Database.ExecuteSqlRaw(
- """
+ await ctx.Database.ExecuteSqlAsync(
+ $$"""
INSERT INTO `Entities` (`Json`, `Id`)
VALUES(
-'{{"Collection":[{{"Bar":11,"Foo":"c11"}},{{"Bar":12,"Foo":"c12"}},{{"Bar":13,"Foo":"c13"}}]}}',
+N'{"Collection":[{"Bar":11,"Foo":"c11"},{"Bar":12,"Foo":"c12"},{"Bar":13,"Foo":"c13"}]}',
1)
""");
- ctx.Database.ExecuteSqlRaw(
- """
+ await ctx.Database.ExecuteSqlAsync(
+ $$$"""
INSERT INTO `Entities` (`Json`, `Id`)
VALUES(
-'{{"Collection":[{{"Bar":21,"Foo":"c21"}},{{"Bar":22,"Foo":"c22"}}]}}',
+N'{"Collection":[{"Bar":21,"Foo":"c21"},{"Bar":22,"Foo":"c22"}]}',
2)
""");
}
- [ConditionalTheory, MemberData(nameof(IsAsyncData))]
- public virtual async Task Json_predicate_on_bytea(bool async)
+ #region EnumLegacyValues
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual async Task Read_enum_property_with_legacy_values(bool async)
{
- var contextFactory = await InitializeAsync(
- seed: context =>
- {
- context.Entities.AddRange(
- new TypesContainerEntity { JsonEntity = new TypesJsonEntity { Bytea = [1, 2, 3] } },
- new TypesContainerEntity { JsonEntity = new TypesJsonEntity { Bytea = [1, 2, 4] } });
- context.SaveChanges();
- });
+ var contextFactory = await InitializeAsync(
+ onModelCreating: BuildModelEnumLegacyValues,
+ onConfiguring: b => b.ConfigureWarnings(ConfigureWarnings),
+ seed: SeedEnumLegacyValues);
using (var context = contextFactory.CreateContext())
{
- var query = context.Entities.Where(x => x.JsonEntity.Bytea == new byte[] { 1, 2, 4 });
+ var query = context.Set().Select(
+ x => new
+ {
+ x.Reference.IntEnum,
+ x.Reference.ByteEnum,
+ x.Reference.LongEnum,
+ x.Reference.NullableEnum
+ });
+
+ var exception = async
+ ? await (Assert.ThrowsAsync(() => query.ToListAsync()))
+ : Assert.Throws(() => query.ToList());
+
+ // Conversion failed when converting the nvarchar value '...' to data type int
+ Assert.Equal(245, exception.Number);
+ }
+ }
- var result = async
- ? await query.SingleAsync()
- : query.Single();
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual async Task Read_json_entity_with_enum_properties_with_legacy_values(bool async)
+ {
+ var contextFactory = await InitializeAsync(
+ onModelCreating: BuildModelEnumLegacyValues,
+ onConfiguring: b => b.ConfigureWarnings(ConfigureWarnings),
+ seed: SeedEnumLegacyValues,
+ shouldLogCategory: c => c == DbLoggerCategory.Query.Name);
- Assert.Equal(2, result.Id);
+ using (var context = contextFactory.CreateContext())
+ {
+ var query = context.Set().Select(x => x.Reference).AsNoTracking();
- AssertSql(
- """
-SELECT e.`Id`, e.`JsonEntity`
-FROM `Entities` AS e
-WHERE (decode(e.`JsonEntity` ->> 'Bytea', 'base64')) = BYTEA E'\\x010204'
-LIMIT 2
-""");
+ var result = async
+ ? await query.ToListAsync()
+ : query.ToList();
+
+ Assert.Equal(1, result.Count);
+ Assert.Equal(ByteEnumLegacyValues.Redmond, result[0].ByteEnum);
+ Assert.Equal(IntEnumLegacyValues.Foo, result[0].IntEnum);
+ Assert.Equal(LongEnumLegacyValues.Three, result[0].LongEnum);
+ Assert.Equal(ULongEnumLegacyValues.Three, result[0].ULongEnum);
+ Assert.Equal(IntEnumLegacyValues.Bar, result[0].NullableEnum);
}
+
+ var testLogger = new TestLogger();
+ Assert.Single(
+ ListLoggerFactory.Log.Where(
+ l => l.Message == CoreResources.LogStringEnumValueInJson(testLogger).GenerateMessage(nameof(ByteEnumLegacyValues))));
+ Assert.Single(
+ ListLoggerFactory.Log.Where(
+ l => l.Message == CoreResources.LogStringEnumValueInJson(testLogger).GenerateMessage(nameof(IntEnumLegacyValues))));
+ Assert.Single(
+ ListLoggerFactory.Log.Where(
+ l => l.Message == CoreResources.LogStringEnumValueInJson(testLogger).GenerateMessage(nameof(LongEnumLegacyValues))));
+ Assert.Single(
+ ListLoggerFactory.Log.Where(
+ l => l.Message == CoreResources.LogStringEnumValueInJson(testLogger).GenerateMessage(nameof(ULongEnumLegacyValues))));
}
- [ConditionalTheory, MemberData(nameof(IsAsyncData))]
- public virtual async Task Json_predicate_on_interval(bool async)
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual async Task Read_json_entity_collection_with_enum_properties_with_legacy_values(bool async)
{
- var contextFactory = await InitializeAsync(
- seed: context =>
- {
- context.Entities.AddRange(
- new TypesContainerEntity { JsonEntity = new TypesJsonEntity { Interval = new TimeSpan(1, 2, 3, 4, 123, 456) } },
- new TypesContainerEntity { JsonEntity = new TypesJsonEntity { Interval = new TimeSpan(2, 2, 3, 4, 123, 456) } });
- context.SaveChanges();
- });
+ var contextFactory = await InitializeAsync(
+ onModelCreating: BuildModelEnumLegacyValues,
+ onConfiguring: b => b.ConfigureWarnings(ConfigureWarnings),
+ seed: SeedEnumLegacyValues,
+ shouldLogCategory: c => c == DbLoggerCategory.Query.Name);
using (var context = contextFactory.CreateContext())
{
- var query = context.Entities.Where(x => x.JsonEntity.Interval == new TimeSpan(2, 2, 3, 4, 123, 456));
+ var query = context.Set().Select(x => x.Collection).AsNoTracking();
var result = async
- ? await query.SingleAsync()
- : query.Single();
+ ? await query.ToListAsync()
+ : query.ToList();
+
+ Assert.Equal(1, result.Count);
+ Assert.Equal(2, result[0].Count);
+ Assert.Equal(ByteEnumLegacyValues.Bellevue, result[0][0].ByteEnum);
+ Assert.Equal(IntEnumLegacyValues.Foo, result[0][0].IntEnum);
+ Assert.Equal(LongEnumLegacyValues.One, result[0][0].LongEnum);
+ Assert.Equal(ULongEnumLegacyValues.One, result[0][0].ULongEnum);
+ Assert.Equal(IntEnumLegacyValues.Bar, result[0][0].NullableEnum);
+ Assert.Equal(ByteEnumLegacyValues.Seattle, result[0][1].ByteEnum);
+ Assert.Equal(IntEnumLegacyValues.Baz, result[0][1].IntEnum);
+ Assert.Equal(LongEnumLegacyValues.Two, result[0][1].LongEnum);
+ Assert.Equal(ULongEnumLegacyValues.Two, result[0][1].ULongEnum);
+ Assert.Null(result[0][1].NullableEnum);
+ }
- Assert.Equal(2, result.Id);
+ var testLogger = new TestLogger();
+ Assert.Single(
+ ListLoggerFactory.Log.Where(
+ l => l.Message == CoreResources.LogStringEnumValueInJson(testLogger).GenerateMessage(nameof(ByteEnumLegacyValues))));
+ Assert.Single(
+ ListLoggerFactory.Log.Where(
+ l => l.Message == CoreResources.LogStringEnumValueInJson(testLogger).GenerateMessage(nameof(IntEnumLegacyValues))));
+ Assert.Single(
+ ListLoggerFactory.Log.Where(
+ l => l.Message == CoreResources.LogStringEnumValueInJson(testLogger).GenerateMessage(nameof(LongEnumLegacyValues))));
+ Assert.Single(
+ ListLoggerFactory.Log.Where(
+ l => l.Message == CoreResources.LogStringEnumValueInJson(testLogger).GenerateMessage(nameof(ULongEnumLegacyValues))));
+ }
- AssertSql(
- """
-SELECT e.`Id`, e.`JsonEntity`
-FROM `Entities` AS e
-WHERE (CAST(e.`JsonEntity` ->> 'Interval' AS interval)) = INTERVAL '2 02:03:04.123456'
-LIMIT 2
+ private Task SeedEnumLegacyValues(DbContext ctx)
+ => ctx.Database.ExecuteSqlAsync(
+ $$"""
+INSERT INTO `Entities` (`Collection`, `Reference`, `Id`, `Name`)
+VALUES(
+N'[{"ByteEnum":"Bellevue","IntEnum":"Foo","LongEnum":"One","ULongEnum":"One","Name":"e1_c1","NullableEnum":"Bar"},{"ByteEnum":"Seattle","IntEnum":"Baz","LongEnum":"Two","ULongEnum":"Two","Name":"e1_c2","NullableEnum":null}]',
+N'{"ByteEnum":"Redmond","IntEnum":"Foo","LongEnum":"Three","ULongEnum":"Three","Name":"e1_r","NullableEnum":"Bar"}',
+1,
+N'e1')
""");
- }
+
+ protected virtual void BuildModelEnumLegacyValues(ModelBuilder modelBuilder)
+ => modelBuilder.Entity(
+ b =>
+ {
+ b.ToTable("Entities");
+ b.Property(x => x.Id).ValueGeneratedNever();
+ b.OwnsOne(x => x.Reference, b => b.ToJson().HasColumnType(JsonColumnType));
+ b.OwnsMany(x => x.Collection, b => b.ToJson().HasColumnType(JsonColumnType));
+ });
+
+ private class MyEntityEnumLegacyValues
+ {
+ public int Id { get; set; }
+ public string Name { get; set; }
+
+ public MyJsonEntityEnumLegacyValues Reference { get; set; }
+ public List Collection { get; set; }
}
- protected class TypesDbContext(DbContextOptions options) : DbContext(options)
+ private class MyJsonEntityEnumLegacyValues
{
- public DbSet Entities { get; set; }
+ public string Name { get; set; }
+
+ // ReSharper disable once UnusedAutoPropertyAccessor.Local
+ public IntEnumLegacyValues IntEnum { get; set; }
+
+ // ReSharper disable once UnusedAutoPropertyAccessor.Local
+ public ByteEnumLegacyValues ByteEnum { get; set; }
- protected override void OnModelCreating(ModelBuilder modelBuilder)
- => modelBuilder.Entity().OwnsOne(b => b.JsonEntity).ToJson();
+ // ReSharper disable once UnusedAutoPropertyAccessor.Local
+ public LongEnumLegacyValues LongEnum { get; set; }
+
+ // ReSharper disable once UnusedAutoPropertyAccessor.Local
+ public ULongEnumLegacyValues ULongEnum { get; set; }
+
+ // ReSharper disable once UnusedAutoPropertyAccessor.Local
+ public IntEnumLegacyValues? NullableEnum { get; set; }
}
- public class TypesContainerEntity
+ private enum IntEnumLegacyValues
{
- public int Id { get; set; }
- public TypesJsonEntity JsonEntity { get; set; }
+ Foo = int.MinValue,
+ Bar,
+ Baz = int.MaxValue,
+ }
+
+ private enum ByteEnumLegacyValues : byte
+ {
+ Seattle,
+ Redmond,
+ Bellevue = 255,
+ }
+
+ private enum LongEnumLegacyValues : long
+ {
+ One = long.MinValue,
+ Two = 1,
+ Three = long.MaxValue,
}
- public class TypesJsonEntity
+ private enum ULongEnumLegacyValues : ulong
{
- public byte[] Bytea { get; set; }
- public TimeSpan Interval { get; set; }
+ One = ulong.MinValue,
+ Two = 1,
+ Three = ulong.MaxValue,
}
- protected void AssertSql(params string[] expected)
- => TestSqlLoggerFactory.AssertBaseline(expected);
+ #endregion
}
diff --git a/test/EFCore.MySql.FunctionalTests/Query/AdHocMiscellaneousQueryMySqlTest.cs b/test/EFCore.MySql.FunctionalTests/Query/AdHocMiscellaneousQueryMySqlTest.cs
index 2235fb2a9..0a1781252 100644
--- a/test/EFCore.MySql.FunctionalTests/Query/AdHocMiscellaneousQueryMySqlTest.cs
+++ b/test/EFCore.MySql.FunctionalTests/Query/AdHocMiscellaneousQueryMySqlTest.cs
@@ -13,8 +13,8 @@ public class AdHocMiscellaneousQueryMySqlTest : AdHocMiscellaneousQueryRelationa
protected override ITestStoreFactory TestStoreFactory
=> MySqlTestStoreFactory.Instance;
- protected override void Seed2951(Context2951 context)
- => context.Database.ExecuteSqlRaw(
+ protected override Task Seed2951(Context2951 context)
+ => context.Database.ExecuteSqlRawAsync(
"""
CREATE TABLE `ZeroKey` (`Id` int);
INSERT INTO `ZeroKey` VALUES (NULL)
diff --git a/test/EFCore.MySql.FunctionalTests/Query/CaseSensitiveNorthwindQueryMySqlFixture.cs b/test/EFCore.MySql.FunctionalTests/Query/CaseSensitiveNorthwindQueryMySqlFixture.cs
index 4ccce05bf..4db2a8247 100644
--- a/test/EFCore.MySql.FunctionalTests/Query/CaseSensitiveNorthwindQueryMySqlFixture.cs
+++ b/test/EFCore.MySql.FunctionalTests/Query/CaseSensitiveNorthwindQueryMySqlFixture.cs
@@ -1,21 +1,13 @@
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.TestUtilities;
using Pomelo.EntityFrameworkCore.MySql.FunctionalTests.TestUtilities;
namespace Pomelo.EntityFrameworkCore.MySql.FunctionalTests.Query
{
public class CaseSensitiveNorthwindQueryMySqlFixture : NorthwindQueryMySqlFixture
- where TModelCustomizer : IModelCustomizer, new()
+ where TModelCustomizer : ITestModelCustomizer, new()
{
protected override string StoreName => "NorthwindCs";
protected override ITestStoreFactory TestStoreFactory => MySqlNorthwindTestStoreFactory.InstanceCs;
-
- public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder)
- {
- var optionsBuilder = base.AddOptions(builder);
- new MySqlDbContextOptionsBuilder(optionsBuilder).EnableStringComparisonTranslations();
- return optionsBuilder;
- }
}
}
diff --git a/test/EFCore.MySql.FunctionalTests/Query/CaseSensitiveWithStringComparisonNorthwindQueryMySqlFixture.cs b/test/EFCore.MySql.FunctionalTests/Query/CaseSensitiveWithStringComparisonNorthwindQueryMySqlFixture.cs
new file mode 100644
index 000000000..b766474cb
--- /dev/null
+++ b/test/EFCore.MySql.FunctionalTests/Query/CaseSensitiveWithStringComparisonNorthwindQueryMySqlFixture.cs
@@ -0,0 +1,15 @@
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+
+namespace Pomelo.EntityFrameworkCore.MySql.FunctionalTests.Query;
+
+public class CaseSensitiveWithStringComparisonNorthwindQueryMySqlFixture : CaseSensitiveNorthwindQueryMySqlFixture
+ where TModelCustomizer : ITestModelCustomizer, new()
+{
+ public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder)
+ {
+ var optionsBuilder = base.AddOptions(builder);
+ new MySqlDbContextOptionsBuilder(optionsBuilder).EnableStringComparisonTranslations();
+ return optionsBuilder;
+ }
+}
diff --git a/test/EFCore.MySql.FunctionalTests/Query/ComplexNavigationsCollectionsSplitQueryMySqlTest.cs b/test/EFCore.MySql.FunctionalTests/Query/ComplexNavigationsCollectionsSplitQueryMySqlTest.cs
index 23f47e655..6d0841297 100644
--- a/test/EFCore.MySql.FunctionalTests/Query/ComplexNavigationsCollectionsSplitQueryMySqlTest.cs
+++ b/test/EFCore.MySql.FunctionalTests/Query/ComplexNavigationsCollectionsSplitQueryMySqlTest.cs
@@ -2426,13 +2426,10 @@ public override async Task Include_collection_with_groupby_in_subquery_and_filte
"""
SELECT `l3`.`Id`, `l3`.`Date`, `l3`.`Name`, `l3`.`OneToMany_Optional_Self_Inverse1Id`, `l3`.`OneToMany_Required_Self_Inverse1Id`, `l3`.`OneToOne_Optional_Self1Id`, `l1`.`Name`
FROM (
- SELECT `l10`.`Name`
- FROM (
- SELECT `l`.`Name`, (`l`.`Name` <> 'Foo') OR `l`.`Name` IS NULL AS `c`
- FROM `LevelOne` AS `l`
- GROUP BY `l`.`Name`, `c`
- HAVING `c`
- ) AS `l10`
+ SELECT `l`.`Name`, (`l`.`Name` <> 'Foo') OR `l`.`Name` IS NULL AS `c`
+ FROM `LevelOne` AS `l`
+ GROUP BY `l`.`Name`, `c`
+ HAVING `c`
) AS `l1`
LEFT JOIN (
SELECT `l2`.`Id`, `l2`.`Date`, `l2`.`Name`, `l2`.`OneToMany_Optional_Self_Inverse1Id`, `l2`.`OneToMany_Required_Self_Inverse1Id`, `l2`.`OneToOne_Optional_Self1Id`
@@ -2448,13 +2445,10 @@ LEFT JOIN (
"""
SELECT `l6`.`Id`, `l6`.`Date`, `l6`.`Level1_Optional_Id`, `l6`.`Level1_Required_Id`, `l6`.`Name`, `l6`.`OneToMany_Optional_Inverse2Id`, `l6`.`OneToMany_Optional_Self_Inverse2Id`, `l6`.`OneToMany_Required_Inverse2Id`, `l6`.`OneToMany_Required_Self_Inverse2Id`, `l6`.`OneToOne_Optional_PK_Inverse2Id`, `l6`.`OneToOne_Optional_Self2Id`, `l7`.`Name`, `l9`.`Id`
FROM (
- SELECT `l11`.`Name`
- FROM (
- SELECT `l`.`Name`, (`l`.`Name` <> 'Foo') OR `l`.`Name` IS NULL AS `c`
- FROM `LevelOne` AS `l`
- GROUP BY `l`.`Name`, `c`
- HAVING `c`
- ) AS `l11`
+ SELECT `l`.`Name`, (`l`.`Name` <> 'Foo') OR `l`.`Name` IS NULL AS `c`
+ FROM `LevelOne` AS `l`
+ GROUP BY `l`.`Name`, `c`
+ HAVING `c`
) AS `l7`
LEFT JOIN (
SELECT `l8`.`Id`, `l8`.`Name`
diff --git a/test/EFCore.MySql.FunctionalTests/Query/ComplexNavigationsQueryMySqlTest.cs b/test/EFCore.MySql.FunctionalTests/Query/ComplexNavigationsQueryMySqlTest.cs
index 5c46ba16d..6699e4446 100644
--- a/test/EFCore.MySql.FunctionalTests/Query/ComplexNavigationsQueryMySqlTest.cs
+++ b/test/EFCore.MySql.FunctionalTests/Query/ComplexNavigationsQueryMySqlTest.cs
@@ -21,9 +21,6 @@ public ComplexNavigationsQueryMySqlTest(ComplexNavigationsQueryMySqlFixture fixt
//Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper);
}
- protected override bool CanExecuteQueryString
- => true;
-
[SupportedServerVersionCondition(nameof(ServerVersionSupport.OuterReferenceInMultiLevelSubquery))]
public override Task Contains_with_subquery_optional_navigation_and_constant_item(bool async)
{
diff --git a/test/EFCore.MySql.FunctionalTests/Query/ComplexNavigationsSharedTypeQueryMySqlTest.cs b/test/EFCore.MySql.FunctionalTests/Query/ComplexNavigationsSharedTypeQueryMySqlTest.cs
index 3c2954f2b..f8af953af 100644
--- a/test/EFCore.MySql.FunctionalTests/Query/ComplexNavigationsSharedTypeQueryMySqlTest.cs
+++ b/test/EFCore.MySql.FunctionalTests/Query/ComplexNavigationsSharedTypeQueryMySqlTest.cs
@@ -33,26 +33,6 @@ public override Task Contains_with_subquery_optional_navigation_and_constant_ite
return base.Contains_with_subquery_optional_navigation_and_constant_item(async);
}
- public override Task Distinct_take_without_orderby(bool async)
- {
- return AssertQuery(
- async,
- ss => from l1 in ss.Set()
- where l1.Id < 3
- select (from l3 in ss.Set()
- select l3).Distinct().OrderBy(e => e.Id).Take(1).FirstOrDefault().Name); // Apply OrderBy before Skip
- }
-
- public override Task Distinct_skip_without_orderby(bool async)
- {
- return AssertQuery(
- async,
- ss => from l1 in ss.Set()
- where l1.Id < 3
- select (from l3 in ss.Set()
- select l3).Distinct().OrderBy(e => e.Id).Skip(1).FirstOrDefault().Name); // Apply OrderBy before Skip
- }
-
public override async Task SelectMany_subquery_with_custom_projection(bool async)
{
// TODO: Fix test in EF Core upstream.
diff --git a/test/EFCore.MySql.FunctionalTests/Query/ComplexTypeQueryMySqlTest.cs b/test/EFCore.MySql.FunctionalTests/Query/ComplexTypeQueryMySqlTest.cs
index c7e021390..5be666351 100644
--- a/test/EFCore.MySql.FunctionalTests/Query/ComplexTypeQueryMySqlTest.cs
+++ b/test/EFCore.MySql.FunctionalTests/Query/ComplexTypeQueryMySqlTest.cs
@@ -238,12 +238,12 @@ public override async Task Complex_type_equals_parameter(bool async)
@__entity_equality_address_0_AddressLine1='804 S. Lakeshore Road' (Size = 4000)
@__entity_equality_address_0_Tags='["foo","bar"]' (Size = 4000)
@__entity_equality_address_0_ZipCode='38654' (Nullable = true)
-@__entity_equality_address_0_Code='US' (Size = 4000)
-@__entity_equality_address_0_FullName='United States' (Size = 4000)
+@__entity_equality_address_0_Country_Code='US' (Size = 4000)
+@__entity_equality_address_0_Country_FullName='United States' (Size = 4000)
SELECT `c`.`Id`, `c`.`Name`, `c`.`BillingAddress_AddressLine1`, `c`.`BillingAddress_AddressLine2`, `c`.`BillingAddress_Tags`, `c`.`BillingAddress_ZipCode`, `c`.`BillingAddress_Country_Code`, `c`.`BillingAddress_Country_FullName`, `c`.`ShippingAddress_AddressLine1`, `c`.`ShippingAddress_AddressLine2`, `c`.`ShippingAddress_Tags`, `c`.`ShippingAddress_ZipCode`, `c`.`ShippingAddress_Country_Code`, `c`.`ShippingAddress_Country_FullName`
FROM `Customer` AS `c`
-WHERE (((((`c`.`ShippingAddress_AddressLine1` = @__entity_equality_address_0_AddressLine1) AND `c`.`ShippingAddress_AddressLine2` IS NULL) AND (`c`.`ShippingAddress_Tags` = @__entity_equality_address_0_Tags)) AND (`c`.`ShippingAddress_ZipCode` = @__entity_equality_address_0_ZipCode)) AND (`c`.`ShippingAddress_Country_Code` = @__entity_equality_address_0_Code)) AND (`c`.`ShippingAddress_Country_FullName` = @__entity_equality_address_0_FullName)
+WHERE (((((`c`.`ShippingAddress_AddressLine1` = @__entity_equality_address_0_AddressLine1) AND `c`.`ShippingAddress_AddressLine2` IS NULL) AND (`c`.`ShippingAddress_Tags` = @__entity_equality_address_0_Tags)) AND (`c`.`ShippingAddress_ZipCode` = @__entity_equality_address_0_ZipCode)) AND (`c`.`ShippingAddress_Country_Code` = @__entity_equality_address_0_Country_Code)) AND (`c`.`ShippingAddress_Country_FullName` = @__entity_equality_address_0_Country_FullName)
""");
}
@@ -264,15 +264,15 @@ public override async Task Contains_over_complex_type(bool async)
@__entity_equality_address_0_AddressLine1='804 S. Lakeshore Road' (Size = 4000)
@__entity_equality_address_0_Tags='["foo","bar"]' (Size = 4000)
@__entity_equality_address_0_ZipCode='38654' (Nullable = true)
-@__entity_equality_address_0_Code='US' (Size = 4000)
-@__entity_equality_address_0_FullName='United States' (Size = 4000)
+@__entity_equality_address_0_Country_Code='US' (Size = 4000)
+@__entity_equality_address_0_Country_FullName='United States' (Size = 4000)
SELECT `c`.`Id`, `c`.`Name`, `c`.`BillingAddress_AddressLine1`, `c`.`BillingAddress_AddressLine2`, `c`.`BillingAddress_Tags`, `c`.`BillingAddress_ZipCode`, `c`.`BillingAddress_Country_Code`, `c`.`BillingAddress_Country_FullName`, `c`.`ShippingAddress_AddressLine1`, `c`.`ShippingAddress_AddressLine2`, `c`.`ShippingAddress_Tags`, `c`.`ShippingAddress_ZipCode`, `c`.`ShippingAddress_Country_Code`, `c`.`ShippingAddress_Country_FullName`
FROM `Customer` AS `c`
WHERE EXISTS (
SELECT 1
FROM `Customer` AS `c0`
- WHERE (((((`c0`.`ShippingAddress_AddressLine1` = @__entity_equality_address_0_AddressLine1) AND `c0`.`ShippingAddress_AddressLine2` IS NULL) AND (`c0`.`ShippingAddress_Tags` = @__entity_equality_address_0_Tags)) AND (`c0`.`ShippingAddress_ZipCode` = @__entity_equality_address_0_ZipCode)) AND (`c0`.`ShippingAddress_Country_Code` = @__entity_equality_address_0_Code)) AND (`c0`.`ShippingAddress_Country_FullName` = @__entity_equality_address_0_FullName))
+ WHERE (((((`c0`.`ShippingAddress_AddressLine1` = @__entity_equality_address_0_AddressLine1) AND `c0`.`ShippingAddress_AddressLine2` IS NULL) AND (`c0`.`ShippingAddress_Tags` = @__entity_equality_address_0_Tags)) AND (`c0`.`ShippingAddress_ZipCode` = @__entity_equality_address_0_ZipCode)) AND (`c0`.`ShippingAddress_Country_Code` = @__entity_equality_address_0_Country_Code)) AND (`c0`.`ShippingAddress_Country_FullName` = @__entity_equality_address_0_Country_FullName))
""");
}
@@ -628,12 +628,12 @@ public override async Task Struct_complex_type_equals_parameter(bool async)
"""
@__entity_equality_address_0_AddressLine1='804 S. Lakeshore Road' (Size = 4000)
@__entity_equality_address_0_ZipCode='38654' (Nullable = true)
-@__entity_equality_address_0_Code='US' (Size = 4000)
-@__entity_equality_address_0_FullName='United States' (Size = 4000)
+@__entity_equality_address_0_Country_Code='US' (Size = 4000)
+@__entity_equality_address_0_Country_FullName='United States' (Size = 4000)
SELECT `v`.`Id`, `v`.`Name`, `v`.`BillingAddress_AddressLine1`, `v`.`BillingAddress_AddressLine2`, `v`.`BillingAddress_ZipCode`, `v`.`BillingAddress_Country_Code`, `v`.`BillingAddress_Country_FullName`, `v`.`ShippingAddress_AddressLine1`, `v`.`ShippingAddress_AddressLine2`, `v`.`ShippingAddress_ZipCode`, `v`.`ShippingAddress_Country_Code`, `v`.`ShippingAddress_Country_FullName`
FROM `ValuedCustomer` AS `v`
-WHERE ((((`v`.`ShippingAddress_AddressLine1` = @__entity_equality_address_0_AddressLine1) AND `v`.`ShippingAddress_AddressLine2` IS NULL) AND (`v`.`ShippingAddress_ZipCode` = @__entity_equality_address_0_ZipCode)) AND (`v`.`ShippingAddress_Country_Code` = @__entity_equality_address_0_Code)) AND (`v`.`ShippingAddress_Country_FullName` = @__entity_equality_address_0_FullName)
+WHERE ((((`v`.`ShippingAddress_AddressLine1` = @__entity_equality_address_0_AddressLine1) AND `v`.`ShippingAddress_AddressLine2` IS NULL) AND (`v`.`ShippingAddress_ZipCode` = @__entity_equality_address_0_ZipCode)) AND (`v`.`ShippingAddress_Country_Code` = @__entity_equality_address_0_Country_Code)) AND (`v`.`ShippingAddress_Country_FullName` = @__entity_equality_address_0_Country_FullName)
""");
}
@@ -645,15 +645,15 @@ public override async Task Contains_over_struct_complex_type(bool async)
"""
@__entity_equality_address_0_AddressLine1='804 S. Lakeshore Road' (Size = 4000)
@__entity_equality_address_0_ZipCode='38654' (Nullable = true)
-@__entity_equality_address_0_Code='US' (Size = 4000)
-@__entity_equality_address_0_FullName='United States' (Size = 4000)
+@__entity_equality_address_0_Country_Code='US' (Size = 4000)
+@__entity_equality_address_0_Country_FullName='United States' (Size = 4000)
SELECT `v`.`Id`, `v`.`Name`, `v`.`BillingAddress_AddressLine1`, `v`.`BillingAddress_AddressLine2`, `v`.`BillingAddress_ZipCode`, `v`.`BillingAddress_Country_Code`, `v`.`BillingAddress_Country_FullName`, `v`.`ShippingAddress_AddressLine1`, `v`.`ShippingAddress_AddressLine2`, `v`.`ShippingAddress_ZipCode`, `v`.`ShippingAddress_Country_Code`, `v`.`ShippingAddress_Country_FullName`
FROM `ValuedCustomer` AS `v`
WHERE EXISTS (
SELECT 1
FROM `ValuedCustomer` AS `v0`
- WHERE ((((`v0`.`ShippingAddress_AddressLine1` = @__entity_equality_address_0_AddressLine1) AND `v0`.`ShippingAddress_AddressLine2` IS NULL) AND (`v0`.`ShippingAddress_ZipCode` = @__entity_equality_address_0_ZipCode)) AND (`v0`.`ShippingAddress_Country_Code` = @__entity_equality_address_0_Code)) AND (`v0`.`ShippingAddress_Country_FullName` = @__entity_equality_address_0_FullName))
+ WHERE ((((`v0`.`ShippingAddress_AddressLine1` = @__entity_equality_address_0_AddressLine1) AND `v0`.`ShippingAddress_AddressLine2` IS NULL) AND (`v0`.`ShippingAddress_ZipCode` = @__entity_equality_address_0_ZipCode)) AND (`v0`.`ShippingAddress_Country_Code` = @__entity_equality_address_0_Country_Code)) AND (`v0`.`ShippingAddress_Country_FullName` = @__entity_equality_address_0_Country_FullName))
""");
}
@@ -748,9 +748,418 @@ public override async Task Union_property_in_struct_complex_type(bool async)
""");
}
+ public override async Task Project_same_nested_complex_type_twice_with_pushdown(bool async)
+ {
+ await base.Project_same_nested_complex_type_twice_with_pushdown(async);
+
+ AssertSql(
+"""
+SELECT `s`.`BillingAddress_AddressLine1`, `s`.`BillingAddress_AddressLine2`, `s`.`BillingAddress_Tags`, `s`.`BillingAddress_ZipCode`, `s`.`BillingAddress_Country_Code`, `s`.`BillingAddress_Country_FullName`, `s`.`BillingAddress_AddressLine10`, `s`.`BillingAddress_AddressLine20`, `s`.`BillingAddress_Tags0`, `s`.`BillingAddress_ZipCode0`, `s`.`BillingAddress_Country_Code0`, `s`.`BillingAddress_Country_FullName0`
+FROM (
+ SELECT DISTINCT `c`.`BillingAddress_AddressLine1`, `c`.`BillingAddress_AddressLine2`, `c`.`BillingAddress_Tags`, `c`.`BillingAddress_ZipCode`, `c`.`BillingAddress_Country_Code`, `c`.`BillingAddress_Country_FullName`, `c0`.`BillingAddress_AddressLine1` AS `BillingAddress_AddressLine10`, `c0`.`BillingAddress_AddressLine2` AS `BillingAddress_AddressLine20`, `c0`.`BillingAddress_Tags` AS `BillingAddress_Tags0`, `c0`.`BillingAddress_ZipCode` AS `BillingAddress_ZipCode0`, `c0`.`BillingAddress_Country_Code` AS `BillingAddress_Country_Code0`, `c0`.`BillingAddress_Country_FullName` AS `BillingAddress_Country_FullName0`
+ FROM `Customer` AS `c`
+ CROSS JOIN `Customer` AS `c0`
+) AS `s`
+""");
+ }
+
+ public override async Task Project_same_entity_with_nested_complex_type_twice_with_pushdown(bool async)
+ {
+ await base.Project_same_entity_with_nested_complex_type_twice_with_pushdown(async);
+
+ AssertSql(
+"""
+SELECT `s`.`Id`, `s`.`Name`, `s`.`BillingAddress_AddressLine1`, `s`.`BillingAddress_AddressLine2`, `s`.`BillingAddress_Tags`, `s`.`BillingAddress_ZipCode`, `s`.`BillingAddress_Country_Code`, `s`.`BillingAddress_Country_FullName`, `s`.`ShippingAddress_AddressLine1`, `s`.`ShippingAddress_AddressLine2`, `s`.`ShippingAddress_Tags`, `s`.`ShippingAddress_ZipCode`, `s`.`ShippingAddress_Country_Code`, `s`.`ShippingAddress_Country_FullName`, `s`.`Id0`, `s`.`Name0`, `s`.`BillingAddress_AddressLine10`, `s`.`BillingAddress_AddressLine20`, `s`.`BillingAddress_Tags0`, `s`.`BillingAddress_ZipCode0`, `s`.`BillingAddress_Country_Code0`, `s`.`BillingAddress_Country_FullName0`, `s`.`ShippingAddress_AddressLine10`, `s`.`ShippingAddress_AddressLine20`, `s`.`ShippingAddress_Tags0`, `s`.`ShippingAddress_ZipCode0`, `s`.`ShippingAddress_Country_Code0`, `s`.`ShippingAddress_Country_FullName0`
+FROM (
+ SELECT DISTINCT `c`.`Id`, `c`.`Name`, `c`.`BillingAddress_AddressLine1`, `c`.`BillingAddress_AddressLine2`, `c`.`BillingAddress_Tags`, `c`.`BillingAddress_ZipCode`, `c`.`BillingAddress_Country_Code`, `c`.`BillingAddress_Country_FullName`, `c`.`ShippingAddress_AddressLine1`, `c`.`ShippingAddress_AddressLine2`, `c`.`ShippingAddress_Tags`, `c`.`ShippingAddress_ZipCode`, `c`.`ShippingAddress_Country_Code`, `c`.`ShippingAddress_Country_FullName`, `c0`.`Id` AS `Id0`, `c0`.`Name` AS `Name0`, `c0`.`BillingAddress_AddressLine1` AS `BillingAddress_AddressLine10`, `c0`.`BillingAddress_AddressLine2` AS `BillingAddress_AddressLine20`, `c0`.`BillingAddress_Tags` AS `BillingAddress_Tags0`, `c0`.`BillingAddress_ZipCode` AS `BillingAddress_ZipCode0`, `c0`.`BillingAddress_Country_Code` AS `BillingAddress_Country_Code0`, `c0`.`BillingAddress_Country_FullName` AS `BillingAddress_Country_FullName0`, `c0`.`ShippingAddress_AddressLine1` AS `ShippingAddress_AddressLine10`, `c0`.`ShippingAddress_AddressLine2` AS `ShippingAddress_AddressLine20`, `c0`.`ShippingAddress_Tags` AS `ShippingAddress_Tags0`, `c0`.`ShippingAddress_ZipCode` AS `ShippingAddress_ZipCode0`, `c0`.`ShippingAddress_Country_Code` AS `ShippingAddress_Country_Code0`, `c0`.`ShippingAddress_Country_FullName` AS `ShippingAddress_Country_FullName0`
+ FROM `Customer` AS `c`
+ CROSS JOIN `Customer` AS `c0`
+) AS `s`
+""");
+ }
+
+ public override async Task Project_same_nested_complex_type_twice_with_double_pushdown(bool async)
+ {
+ await base.Project_same_nested_complex_type_twice_with_double_pushdown(async);
+
+ AssertSql(
+"""
+@__p_0='50'
+
+SELECT `s0`.`BillingAddress_AddressLine1`, `s0`.`BillingAddress_AddressLine2`, `s0`.`BillingAddress_Tags`, `s0`.`BillingAddress_ZipCode`, `s0`.`BillingAddress_Country_Code`, `s0`.`BillingAddress_Country_FullName`, `s0`.`BillingAddress_AddressLine10`, `s0`.`BillingAddress_AddressLine20`, `s0`.`BillingAddress_Tags0`, `s0`.`BillingAddress_ZipCode0`, `s0`.`BillingAddress_Country_Code0`, `s0`.`BillingAddress_Country_FullName0`
+FROM (
+ SELECT DISTINCT `s`.`BillingAddress_AddressLine1`, `s`.`BillingAddress_AddressLine2`, `s`.`BillingAddress_Tags`, `s`.`BillingAddress_ZipCode`, `s`.`BillingAddress_Country_Code`, `s`.`BillingAddress_Country_FullName`, `s`.`BillingAddress_AddressLine10`, `s`.`BillingAddress_AddressLine20`, `s`.`BillingAddress_Tags0`, `s`.`BillingAddress_ZipCode0`, `s`.`BillingAddress_Country_Code0`, `s`.`BillingAddress_Country_FullName0`
+ FROM (
+ SELECT `c`.`BillingAddress_AddressLine1`, `c`.`BillingAddress_AddressLine2`, `c`.`BillingAddress_Tags`, `c`.`BillingAddress_ZipCode`, `c`.`BillingAddress_Country_Code`, `c`.`BillingAddress_Country_FullName`, `c0`.`BillingAddress_AddressLine1` AS `BillingAddress_AddressLine10`, `c0`.`BillingAddress_AddressLine2` AS `BillingAddress_AddressLine20`, `c0`.`BillingAddress_Tags` AS `BillingAddress_Tags0`, `c0`.`BillingAddress_ZipCode` AS `BillingAddress_ZipCode0`, `c0`.`BillingAddress_Country_Code` AS `BillingAddress_Country_Code0`, `c0`.`BillingAddress_Country_FullName` AS `BillingAddress_Country_FullName0`
+ FROM `Customer` AS `c`
+ CROSS JOIN `Customer` AS `c0`
+ ORDER BY `c`.`Id`, `c0`.`Id`
+ LIMIT @__p_0
+ ) AS `s`
+) AS `s0`
+""");
+ }
+
+ public override async Task Project_same_entity_with_nested_complex_type_twice_with_double_pushdown(bool async)
+ {
+ await base.Project_same_entity_with_nested_complex_type_twice_with_double_pushdown(async);
+
+ AssertSql(
+"""
+@__p_0='50'
+
+SELECT `s0`.`Id`, `s0`.`Name`, `s0`.`BillingAddress_AddressLine1`, `s0`.`BillingAddress_AddressLine2`, `s0`.`BillingAddress_Tags`, `s0`.`BillingAddress_ZipCode`, `s0`.`BillingAddress_Country_Code`, `s0`.`BillingAddress_Country_FullName`, `s0`.`ShippingAddress_AddressLine1`, `s0`.`ShippingAddress_AddressLine2`, `s0`.`ShippingAddress_Tags`, `s0`.`ShippingAddress_ZipCode`, `s0`.`ShippingAddress_Country_Code`, `s0`.`ShippingAddress_Country_FullName`, `s0`.`Id0`, `s0`.`Name0`, `s0`.`BillingAddress_AddressLine10`, `s0`.`BillingAddress_AddressLine20`, `s0`.`BillingAddress_Tags0`, `s0`.`BillingAddress_ZipCode0`, `s0`.`BillingAddress_Country_Code0`, `s0`.`BillingAddress_Country_FullName0`, `s0`.`ShippingAddress_AddressLine10`, `s0`.`ShippingAddress_AddressLine20`, `s0`.`ShippingAddress_Tags0`, `s0`.`ShippingAddress_ZipCode0`, `s0`.`ShippingAddress_Country_Code0`, `s0`.`ShippingAddress_Country_FullName0`
+FROM (
+ SELECT DISTINCT `s`.`Id`, `s`.`Name`, `s`.`BillingAddress_AddressLine1`, `s`.`BillingAddress_AddressLine2`, `s`.`BillingAddress_Tags`, `s`.`BillingAddress_ZipCode`, `s`.`BillingAddress_Country_Code`, `s`.`BillingAddress_Country_FullName`, `s`.`ShippingAddress_AddressLine1`, `s`.`ShippingAddress_AddressLine2`, `s`.`ShippingAddress_Tags`, `s`.`ShippingAddress_ZipCode`, `s`.`ShippingAddress_Country_Code`, `s`.`ShippingAddress_Country_FullName`, `s`.`Id0`, `s`.`Name0`, `s`.`BillingAddress_AddressLine10`, `s`.`BillingAddress_AddressLine20`, `s`.`BillingAddress_Tags0`, `s`.`BillingAddress_ZipCode0`, `s`.`BillingAddress_Country_Code0`, `s`.`BillingAddress_Country_FullName0`, `s`.`ShippingAddress_AddressLine10`, `s`.`ShippingAddress_AddressLine20`, `s`.`ShippingAddress_Tags0`, `s`.`ShippingAddress_ZipCode0`, `s`.`ShippingAddress_Country_Code0`, `s`.`ShippingAddress_Country_FullName0`
+ FROM (
+ SELECT `c`.`Id`, `c`.`Name`, `c`.`BillingAddress_AddressLine1`, `c`.`BillingAddress_AddressLine2`, `c`.`BillingAddress_Tags`, `c`.`BillingAddress_ZipCode`, `c`.`BillingAddress_Country_Code`, `c`.`BillingAddress_Country_FullName`, `c`.`ShippingAddress_AddressLine1`, `c`.`ShippingAddress_AddressLine2`, `c`.`ShippingAddress_Tags`, `c`.`ShippingAddress_ZipCode`, `c`.`ShippingAddress_Country_Code`, `c`.`ShippingAddress_Country_FullName`, `c0`.`Id` AS `Id0`, `c0`.`Name` AS `Name0`, `c0`.`BillingAddress_AddressLine1` AS `BillingAddress_AddressLine10`, `c0`.`BillingAddress_AddressLine2` AS `BillingAddress_AddressLine20`, `c0`.`BillingAddress_Tags` AS `BillingAddress_Tags0`, `c0`.`BillingAddress_ZipCode` AS `BillingAddress_ZipCode0`, `c0`.`BillingAddress_Country_Code` AS `BillingAddress_Country_Code0`, `c0`.`BillingAddress_Country_FullName` AS `BillingAddress_Country_FullName0`, `c0`.`ShippingAddress_AddressLine1` AS `ShippingAddress_AddressLine10`, `c0`.`ShippingAddress_AddressLine2` AS `ShippingAddress_AddressLine20`, `c0`.`ShippingAddress_Tags` AS `ShippingAddress_Tags0`, `c0`.`ShippingAddress_ZipCode` AS `ShippingAddress_ZipCode0`, `c0`.`ShippingAddress_Country_Code` AS `ShippingAddress_Country_Code0`, `c0`.`ShippingAddress_Country_FullName` AS `ShippingAddress_Country_FullName0`
+ FROM `Customer` AS `c`
+ CROSS JOIN `Customer` AS `c0`
+ ORDER BY `c`.`Id`, `c0`.`Id`
+ LIMIT @__p_0
+ ) AS `s`
+) AS `s0`
+""");
+ }
+
+ public override async Task Project_same_struct_nested_complex_type_twice_with_pushdown(bool async)
+ {
+ await base.Project_same_struct_nested_complex_type_twice_with_pushdown(async);
+
+ AssertSql(
+"""
+SELECT `s`.`BillingAddress_AddressLine1`, `s`.`BillingAddress_AddressLine2`, `s`.`BillingAddress_ZipCode`, `s`.`BillingAddress_Country_Code`, `s`.`BillingAddress_Country_FullName`, `s`.`BillingAddress_AddressLine10`, `s`.`BillingAddress_AddressLine20`, `s`.`BillingAddress_ZipCode0`, `s`.`BillingAddress_Country_Code0`, `s`.`BillingAddress_Country_FullName0`
+FROM (
+ SELECT DISTINCT `v`.`BillingAddress_AddressLine1`, `v`.`BillingAddress_AddressLine2`, `v`.`BillingAddress_ZipCode`, `v`.`BillingAddress_Country_Code`, `v`.`BillingAddress_Country_FullName`, `v0`.`BillingAddress_AddressLine1` AS `BillingAddress_AddressLine10`, `v0`.`BillingAddress_AddressLine2` AS `BillingAddress_AddressLine20`, `v0`.`BillingAddress_ZipCode` AS `BillingAddress_ZipCode0`, `v0`.`BillingAddress_Country_Code` AS `BillingAddress_Country_Code0`, `v0`.`BillingAddress_Country_FullName` AS `BillingAddress_Country_FullName0`
+ FROM `ValuedCustomer` AS `v`
+ CROSS JOIN `ValuedCustomer` AS `v0`
+) AS `s`
+""");
+ }
+
+ public override async Task Project_same_entity_with_struct_nested_complex_type_twice_with_pushdown(bool async)
+ {
+ await base.Project_same_entity_with_struct_nested_complex_type_twice_with_pushdown(async);
+
+ AssertSql(
+"""
+SELECT `s`.`Id`, `s`.`Name`, `s`.`BillingAddress_AddressLine1`, `s`.`BillingAddress_AddressLine2`, `s`.`BillingAddress_ZipCode`, `s`.`BillingAddress_Country_Code`, `s`.`BillingAddress_Country_FullName`, `s`.`ShippingAddress_AddressLine1`, `s`.`ShippingAddress_AddressLine2`, `s`.`ShippingAddress_ZipCode`, `s`.`ShippingAddress_Country_Code`, `s`.`ShippingAddress_Country_FullName`, `s`.`Id0`, `s`.`Name0`, `s`.`BillingAddress_AddressLine10`, `s`.`BillingAddress_AddressLine20`, `s`.`BillingAddress_ZipCode0`, `s`.`BillingAddress_Country_Code0`, `s`.`BillingAddress_Country_FullName0`, `s`.`ShippingAddress_AddressLine10`, `s`.`ShippingAddress_AddressLine20`, `s`.`ShippingAddress_ZipCode0`, `s`.`ShippingAddress_Country_Code0`, `s`.`ShippingAddress_Country_FullName0`
+FROM (
+ SELECT DISTINCT `v`.`Id`, `v`.`Name`, `v`.`BillingAddress_AddressLine1`, `v`.`BillingAddress_AddressLine2`, `v`.`BillingAddress_ZipCode`, `v`.`BillingAddress_Country_Code`, `v`.`BillingAddress_Country_FullName`, `v`.`ShippingAddress_AddressLine1`, `v`.`ShippingAddress_AddressLine2`, `v`.`ShippingAddress_ZipCode`, `v`.`ShippingAddress_Country_Code`, `v`.`ShippingAddress_Country_FullName`, `v0`.`Id` AS `Id0`, `v0`.`Name` AS `Name0`, `v0`.`BillingAddress_AddressLine1` AS `BillingAddress_AddressLine10`, `v0`.`BillingAddress_AddressLine2` AS `BillingAddress_AddressLine20`, `v0`.`BillingAddress_ZipCode` AS `BillingAddress_ZipCode0`, `v0`.`BillingAddress_Country_Code` AS `BillingAddress_Country_Code0`, `v0`.`BillingAddress_Country_FullName` AS `BillingAddress_Country_FullName0`, `v0`.`ShippingAddress_AddressLine1` AS `ShippingAddress_AddressLine10`, `v0`.`ShippingAddress_AddressLine2` AS `ShippingAddress_AddressLine20`, `v0`.`ShippingAddress_ZipCode` AS `ShippingAddress_ZipCode0`, `v0`.`ShippingAddress_Country_Code` AS `ShippingAddress_Country_Code0`, `v0`.`ShippingAddress_Country_FullName` AS `ShippingAddress_Country_FullName0`
+ FROM `ValuedCustomer` AS `v`
+ CROSS JOIN `ValuedCustomer` AS `v0`
+) AS `s`
+""");
+ }
+
+ public override async Task Project_same_struct_nested_complex_type_twice_with_double_pushdown(bool async)
+ {
+ await base.Project_same_struct_nested_complex_type_twice_with_double_pushdown(async);
+
+ AssertSql(
+"""
+@__p_0='50'
+
+SELECT `s0`.`BillingAddress_AddressLine1`, `s0`.`BillingAddress_AddressLine2`, `s0`.`BillingAddress_ZipCode`, `s0`.`BillingAddress_Country_Code`, `s0`.`BillingAddress_Country_FullName`, `s0`.`BillingAddress_AddressLine10`, `s0`.`BillingAddress_AddressLine20`, `s0`.`BillingAddress_ZipCode0`, `s0`.`BillingAddress_Country_Code0`, `s0`.`BillingAddress_Country_FullName0`
+FROM (
+ SELECT DISTINCT `s`.`BillingAddress_AddressLine1`, `s`.`BillingAddress_AddressLine2`, `s`.`BillingAddress_ZipCode`, `s`.`BillingAddress_Country_Code`, `s`.`BillingAddress_Country_FullName`, `s`.`BillingAddress_AddressLine10`, `s`.`BillingAddress_AddressLine20`, `s`.`BillingAddress_ZipCode0`, `s`.`BillingAddress_Country_Code0`, `s`.`BillingAddress_Country_FullName0`
+ FROM (
+ SELECT `v`.`BillingAddress_AddressLine1`, `v`.`BillingAddress_AddressLine2`, `v`.`BillingAddress_ZipCode`, `v`.`BillingAddress_Country_Code`, `v`.`BillingAddress_Country_FullName`, `v0`.`BillingAddress_AddressLine1` AS `BillingAddress_AddressLine10`, `v0`.`BillingAddress_AddressLine2` AS `BillingAddress_AddressLine20`, `v0`.`BillingAddress_ZipCode` AS `BillingAddress_ZipCode0`, `v0`.`BillingAddress_Country_Code` AS `BillingAddress_Country_Code0`, `v0`.`BillingAddress_Country_FullName` AS `BillingAddress_Country_FullName0`
+ FROM `ValuedCustomer` AS `v`
+ CROSS JOIN `ValuedCustomer` AS `v0`
+ ORDER BY `v`.`Id`, `v0`.`Id`
+ LIMIT @__p_0
+ ) AS `s`
+) AS `s0`
+""");
+ }
+
+ public override async Task Project_same_entity_with_struct_nested_complex_type_twice_with_double_pushdown(bool async)
+ {
+ await base.Project_same_entity_with_struct_nested_complex_type_twice_with_double_pushdown(async);
+
+ AssertSql(
+"""
+@__p_0='50'
+
+SELECT `s0`.`Id`, `s0`.`Name`, `s0`.`BillingAddress_AddressLine1`, `s0`.`BillingAddress_AddressLine2`, `s0`.`BillingAddress_ZipCode`, `s0`.`BillingAddress_Country_Code`, `s0`.`BillingAddress_Country_FullName`, `s0`.`ShippingAddress_AddressLine1`, `s0`.`ShippingAddress_AddressLine2`, `s0`.`ShippingAddress_ZipCode`, `s0`.`ShippingAddress_Country_Code`, `s0`.`ShippingAddress_Country_FullName`, `s0`.`Id0`, `s0`.`Name0`, `s0`.`BillingAddress_AddressLine10`, `s0`.`BillingAddress_AddressLine20`, `s0`.`BillingAddress_ZipCode0`, `s0`.`BillingAddress_Country_Code0`, `s0`.`BillingAddress_Country_FullName0`, `s0`.`ShippingAddress_AddressLine10`, `s0`.`ShippingAddress_AddressLine20`, `s0`.`ShippingAddress_ZipCode0`, `s0`.`ShippingAddress_Country_Code0`, `s0`.`ShippingAddress_Country_FullName0`
+FROM (
+ SELECT DISTINCT `s`.`Id`, `s`.`Name`, `s`.`BillingAddress_AddressLine1`, `s`.`BillingAddress_AddressLine2`, `s`.`BillingAddress_ZipCode`, `s`.`BillingAddress_Country_Code`, `s`.`BillingAddress_Country_FullName`, `s`.`ShippingAddress_AddressLine1`, `s`.`ShippingAddress_AddressLine2`, `s`.`ShippingAddress_ZipCode`, `s`.`ShippingAddress_Country_Code`, `s`.`ShippingAddress_Country_FullName`, `s`.`Id0`, `s`.`Name0`, `s`.`BillingAddress_AddressLine10`, `s`.`BillingAddress_AddressLine20`, `s`.`BillingAddress_ZipCode0`, `s`.`BillingAddress_Country_Code0`, `s`.`BillingAddress_Country_FullName0`, `s`.`ShippingAddress_AddressLine10`, `s`.`ShippingAddress_AddressLine20`, `s`.`ShippingAddress_ZipCode0`, `s`.`ShippingAddress_Country_Code0`, `s`.`ShippingAddress_Country_FullName0`
+ FROM (
+ SELECT `v`.`Id`, `v`.`Name`, `v`.`BillingAddress_AddressLine1`, `v`.`BillingAddress_AddressLine2`, `v`.`BillingAddress_ZipCode`, `v`.`BillingAddress_Country_Code`, `v`.`BillingAddress_Country_FullName`, `v`.`ShippingAddress_AddressLine1`, `v`.`ShippingAddress_AddressLine2`, `v`.`ShippingAddress_ZipCode`, `v`.`ShippingAddress_Country_Code`, `v`.`ShippingAddress_Country_FullName`, `v0`.`Id` AS `Id0`, `v0`.`Name` AS `Name0`, `v0`.`BillingAddress_AddressLine1` AS `BillingAddress_AddressLine10`, `v0`.`BillingAddress_AddressLine2` AS `BillingAddress_AddressLine20`, `v0`.`BillingAddress_ZipCode` AS `BillingAddress_ZipCode0`, `v0`.`BillingAddress_Country_Code` AS `BillingAddress_Country_Code0`, `v0`.`BillingAddress_Country_FullName` AS `BillingAddress_Country_FullName0`, `v0`.`ShippingAddress_AddressLine1` AS `ShippingAddress_AddressLine10`, `v0`.`ShippingAddress_AddressLine2` AS `ShippingAddress_AddressLine20`, `v0`.`ShippingAddress_ZipCode` AS `ShippingAddress_ZipCode0`, `v0`.`ShippingAddress_Country_Code` AS `ShippingAddress_Country_Code0`, `v0`.`ShippingAddress_Country_FullName` AS `ShippingAddress_Country_FullName0`
+ FROM `ValuedCustomer` AS `v`
+ CROSS JOIN `ValuedCustomer` AS `v0`
+ ORDER BY `v`.`Id`, `v0`.`Id`
+ LIMIT @__p_0
+ ) AS `s`
+) AS `s0`
+""");
+ }
+
+ public override async Task Union_of_same_entity_with_nested_complex_type_projected_twice_with_pushdown(bool async)
+ {
+ await base.Union_of_same_entity_with_nested_complex_type_projected_twice_with_pushdown(async);
+
+ AssertSql(
+"""
+@__p_0='50'
+
+SELECT `u`.`Id`, `u`.`Name`, `u`.`BillingAddress_AddressLine1`, `u`.`BillingAddress_AddressLine2`, `u`.`BillingAddress_Tags`, `u`.`BillingAddress_ZipCode`, `u`.`BillingAddress_Country_Code`, `u`.`BillingAddress_Country_FullName`, `u`.`ShippingAddress_AddressLine1`, `u`.`ShippingAddress_AddressLine2`, `u`.`ShippingAddress_Tags`, `u`.`ShippingAddress_ZipCode`, `u`.`ShippingAddress_Country_Code`, `u`.`ShippingAddress_Country_FullName`, `u`.`Id0`, `u`.`Name0`, `u`.`BillingAddress_AddressLine10`, `u`.`BillingAddress_AddressLine20`, `u`.`BillingAddress_Tags0`, `u`.`BillingAddress_ZipCode0`, `u`.`BillingAddress_Country_Code0`, `u`.`BillingAddress_Country_FullName0`, `u`.`ShippingAddress_AddressLine10`, `u`.`ShippingAddress_AddressLine20`, `u`.`ShippingAddress_Tags0`, `u`.`ShippingAddress_ZipCode0`, `u`.`ShippingAddress_Country_Code0`, `u`.`ShippingAddress_Country_FullName0`
+FROM (
+ SELECT `c`.`Id`, `c`.`Name`, `c`.`BillingAddress_AddressLine1`, `c`.`BillingAddress_AddressLine2`, `c`.`BillingAddress_Tags`, `c`.`BillingAddress_ZipCode`, `c`.`BillingAddress_Country_Code`, `c`.`BillingAddress_Country_FullName`, `c`.`ShippingAddress_AddressLine1`, `c`.`ShippingAddress_AddressLine2`, `c`.`ShippingAddress_Tags`, `c`.`ShippingAddress_ZipCode`, `c`.`ShippingAddress_Country_Code`, `c`.`ShippingAddress_Country_FullName`, `c0`.`Id` AS `Id0`, `c0`.`Name` AS `Name0`, `c0`.`BillingAddress_AddressLine1` AS `BillingAddress_AddressLine10`, `c0`.`BillingAddress_AddressLine2` AS `BillingAddress_AddressLine20`, `c0`.`BillingAddress_Tags` AS `BillingAddress_Tags0`, `c0`.`BillingAddress_ZipCode` AS `BillingAddress_ZipCode0`, `c0`.`BillingAddress_Country_Code` AS `BillingAddress_Country_Code0`, `c0`.`BillingAddress_Country_FullName` AS `BillingAddress_Country_FullName0`, `c0`.`ShippingAddress_AddressLine1` AS `ShippingAddress_AddressLine10`, `c0`.`ShippingAddress_AddressLine2` AS `ShippingAddress_AddressLine20`, `c0`.`ShippingAddress_Tags` AS `ShippingAddress_Tags0`, `c0`.`ShippingAddress_ZipCode` AS `ShippingAddress_ZipCode0`, `c0`.`ShippingAddress_Country_Code` AS `ShippingAddress_Country_Code0`, `c0`.`ShippingAddress_Country_FullName` AS `ShippingAddress_Country_FullName0`
+ FROM `Customer` AS `c`
+ CROSS JOIN `Customer` AS `c0`
+ UNION
+ SELECT `c1`.`Id`, `c1`.`Name`, `c1`.`BillingAddress_AddressLine1`, `c1`.`BillingAddress_AddressLine2`, `c1`.`BillingAddress_Tags`, `c1`.`BillingAddress_ZipCode`, `c1`.`BillingAddress_Country_Code`, `c1`.`BillingAddress_Country_FullName`, `c1`.`ShippingAddress_AddressLine1`, `c1`.`ShippingAddress_AddressLine2`, `c1`.`ShippingAddress_Tags`, `c1`.`ShippingAddress_ZipCode`, `c1`.`ShippingAddress_Country_Code`, `c1`.`ShippingAddress_Country_FullName`, `c2`.`Id` AS `Id0`, `c2`.`Name` AS `Name0`, `c2`.`BillingAddress_AddressLine1` AS `BillingAddress_AddressLine10`, `c2`.`BillingAddress_AddressLine2` AS `BillingAddress_AddressLine20`, `c2`.`BillingAddress_Tags` AS `BillingAddress_Tags0`, `c2`.`BillingAddress_ZipCode` AS `BillingAddress_ZipCode0`, `c2`.`BillingAddress_Country_Code` AS `BillingAddress_Country_Code0`, `c2`.`BillingAddress_Country_FullName` AS `BillingAddress_Country_FullName0`, `c2`.`ShippingAddress_AddressLine1` AS `ShippingAddress_AddressLine10`, `c2`.`ShippingAddress_AddressLine2` AS `ShippingAddress_AddressLine20`, `c2`.`ShippingAddress_Tags` AS `ShippingAddress_Tags0`, `c2`.`ShippingAddress_ZipCode` AS `ShippingAddress_ZipCode0`, `c2`.`ShippingAddress_Country_Code` AS `ShippingAddress_Country_Code0`, `c2`.`ShippingAddress_Country_FullName` AS `ShippingAddress_Country_FullName0`
+ FROM `Customer` AS `c1`
+ CROSS JOIN `Customer` AS `c2`
+) AS `u`
+ORDER BY `u`.`Id`, `u`.`Id0`
+LIMIT @__p_0
+""");
+ }
+
+ public override async Task Union_of_same_entity_with_nested_complex_type_projected_twice_with_double_pushdown(bool async)
+ {
+ await base.Union_of_same_entity_with_nested_complex_type_projected_twice_with_double_pushdown(async);
+
+ AssertSql(
+"""
+@__p_0='50'
+
+SELECT `u1`.`Id`, `u1`.`Name`, `u1`.`BillingAddress_AddressLine1`, `u1`.`BillingAddress_AddressLine2`, `u1`.`BillingAddress_Tags`, `u1`.`BillingAddress_ZipCode`, `u1`.`BillingAddress_Country_Code`, `u1`.`BillingAddress_Country_FullName`, `u1`.`ShippingAddress_AddressLine1`, `u1`.`ShippingAddress_AddressLine2`, `u1`.`ShippingAddress_Tags`, `u1`.`ShippingAddress_ZipCode`, `u1`.`ShippingAddress_Country_Code`, `u1`.`ShippingAddress_Country_FullName`, `u1`.`Id0`, `u1`.`Name0`, `u1`.`BillingAddress_AddressLine10`, `u1`.`BillingAddress_AddressLine20`, `u1`.`BillingAddress_Tags0`, `u1`.`BillingAddress_ZipCode0`, `u1`.`BillingAddress_Country_Code0`, `u1`.`BillingAddress_Country_FullName0`, `u1`.`ShippingAddress_AddressLine10`, `u1`.`ShippingAddress_AddressLine20`, `u1`.`ShippingAddress_Tags0`, `u1`.`ShippingAddress_ZipCode0`, `u1`.`ShippingAddress_Country_Code0`, `u1`.`ShippingAddress_Country_FullName0`
+FROM (
+ SELECT DISTINCT `u0`.`Id`, `u0`.`Name`, `u0`.`BillingAddress_AddressLine1`, `u0`.`BillingAddress_AddressLine2`, `u0`.`BillingAddress_Tags`, `u0`.`BillingAddress_ZipCode`, `u0`.`BillingAddress_Country_Code`, `u0`.`BillingAddress_Country_FullName`, `u0`.`ShippingAddress_AddressLine1`, `u0`.`ShippingAddress_AddressLine2`, `u0`.`ShippingAddress_Tags`, `u0`.`ShippingAddress_ZipCode`, `u0`.`ShippingAddress_Country_Code`, `u0`.`ShippingAddress_Country_FullName`, `u0`.`Id0`, `u0`.`Name0`, `u0`.`BillingAddress_AddressLine10`, `u0`.`BillingAddress_AddressLine20`, `u0`.`BillingAddress_Tags0`, `u0`.`BillingAddress_ZipCode0`, `u0`.`BillingAddress_Country_Code0`, `u0`.`BillingAddress_Country_FullName0`, `u0`.`ShippingAddress_AddressLine10`, `u0`.`ShippingAddress_AddressLine20`, `u0`.`ShippingAddress_Tags0`, `u0`.`ShippingAddress_ZipCode0`, `u0`.`ShippingAddress_Country_Code0`, `u0`.`ShippingAddress_Country_FullName0`
+ FROM (
+ SELECT `u`.`Id`, `u`.`Name`, `u`.`BillingAddress_AddressLine1`, `u`.`BillingAddress_AddressLine2`, `u`.`BillingAddress_Tags`, `u`.`BillingAddress_ZipCode`, `u`.`BillingAddress_Country_Code`, `u`.`BillingAddress_Country_FullName`, `u`.`ShippingAddress_AddressLine1`, `u`.`ShippingAddress_AddressLine2`, `u`.`ShippingAddress_Tags`, `u`.`ShippingAddress_ZipCode`, `u`.`ShippingAddress_Country_Code`, `u`.`ShippingAddress_Country_FullName`, `u`.`Id0`, `u`.`Name0`, `u`.`BillingAddress_AddressLine10`, `u`.`BillingAddress_AddressLine20`, `u`.`BillingAddress_Tags0`, `u`.`BillingAddress_ZipCode0`, `u`.`BillingAddress_Country_Code0`, `u`.`BillingAddress_Country_FullName0`, `u`.`ShippingAddress_AddressLine10`, `u`.`ShippingAddress_AddressLine20`, `u`.`ShippingAddress_Tags0`, `u`.`ShippingAddress_ZipCode0`, `u`.`ShippingAddress_Country_Code0`, `u`.`ShippingAddress_Country_FullName0`
+ FROM (
+ SELECT `c`.`Id`, `c`.`Name`, `c`.`BillingAddress_AddressLine1`, `c`.`BillingAddress_AddressLine2`, `c`.`BillingAddress_Tags`, `c`.`BillingAddress_ZipCode`, `c`.`BillingAddress_Country_Code`, `c`.`BillingAddress_Country_FullName`, `c`.`ShippingAddress_AddressLine1`, `c`.`ShippingAddress_AddressLine2`, `c`.`ShippingAddress_Tags`, `c`.`ShippingAddress_ZipCode`, `c`.`ShippingAddress_Country_Code`, `c`.`ShippingAddress_Country_FullName`, `c0`.`Id` AS `Id0`, `c0`.`Name` AS `Name0`, `c0`.`BillingAddress_AddressLine1` AS `BillingAddress_AddressLine10`, `c0`.`BillingAddress_AddressLine2` AS `BillingAddress_AddressLine20`, `c0`.`BillingAddress_Tags` AS `BillingAddress_Tags0`, `c0`.`BillingAddress_ZipCode` AS `BillingAddress_ZipCode0`, `c0`.`BillingAddress_Country_Code` AS `BillingAddress_Country_Code0`, `c0`.`BillingAddress_Country_FullName` AS `BillingAddress_Country_FullName0`, `c0`.`ShippingAddress_AddressLine1` AS `ShippingAddress_AddressLine10`, `c0`.`ShippingAddress_AddressLine2` AS `ShippingAddress_AddressLine20`, `c0`.`ShippingAddress_Tags` AS `ShippingAddress_Tags0`, `c0`.`ShippingAddress_ZipCode` AS `ShippingAddress_ZipCode0`, `c0`.`ShippingAddress_Country_Code` AS `ShippingAddress_Country_Code0`, `c0`.`ShippingAddress_Country_FullName` AS `ShippingAddress_Country_FullName0`
+ FROM `Customer` AS `c`
+ CROSS JOIN `Customer` AS `c0`
+ UNION
+ SELECT `c1`.`Id`, `c1`.`Name`, `c1`.`BillingAddress_AddressLine1`, `c1`.`BillingAddress_AddressLine2`, `c1`.`BillingAddress_Tags`, `c1`.`BillingAddress_ZipCode`, `c1`.`BillingAddress_Country_Code`, `c1`.`BillingAddress_Country_FullName`, `c1`.`ShippingAddress_AddressLine1`, `c1`.`ShippingAddress_AddressLine2`, `c1`.`ShippingAddress_Tags`, `c1`.`ShippingAddress_ZipCode`, `c1`.`ShippingAddress_Country_Code`, `c1`.`ShippingAddress_Country_FullName`, `c2`.`Id` AS `Id0`, `c2`.`Name` AS `Name0`, `c2`.`BillingAddress_AddressLine1` AS `BillingAddress_AddressLine10`, `c2`.`BillingAddress_AddressLine2` AS `BillingAddress_AddressLine20`, `c2`.`BillingAddress_Tags` AS `BillingAddress_Tags0`, `c2`.`BillingAddress_ZipCode` AS `BillingAddress_ZipCode0`, `c2`.`BillingAddress_Country_Code` AS `BillingAddress_Country_Code0`, `c2`.`BillingAddress_Country_FullName` AS `BillingAddress_Country_FullName0`, `c2`.`ShippingAddress_AddressLine1` AS `ShippingAddress_AddressLine10`, `c2`.`ShippingAddress_AddressLine2` AS `ShippingAddress_AddressLine20`, `c2`.`ShippingAddress_Tags` AS `ShippingAddress_Tags0`, `c2`.`ShippingAddress_ZipCode` AS `ShippingAddress_ZipCode0`, `c2`.`ShippingAddress_Country_Code` AS `ShippingAddress_Country_Code0`, `c2`.`ShippingAddress_Country_FullName` AS `ShippingAddress_Country_FullName0`
+ FROM `Customer` AS `c1`
+ CROSS JOIN `Customer` AS `c2`
+ ) AS `u`
+ ORDER BY `u`.`Id`, `u`.`Id0`
+ LIMIT @__p_0
+ ) AS `u0`
+) AS `u1`
+ORDER BY `u1`.`Id`, `u1`.`Id0`
+LIMIT @__p_0
+""");
+ }
+
+ public override async Task Union_of_same_nested_complex_type_projected_twice_with_pushdown(bool async)
+ {
+ await base.Union_of_same_nested_complex_type_projected_twice_with_pushdown(async);
+
+ AssertSql(
+"""
+@__p_0='50'
+
+SELECT `u`.`BillingAddress_AddressLine1`, `u`.`BillingAddress_AddressLine2`, `u`.`BillingAddress_Tags`, `u`.`BillingAddress_ZipCode`, `u`.`BillingAddress_Country_Code`, `u`.`BillingAddress_Country_FullName`, `u`.`BillingAddress_AddressLine10`, `u`.`BillingAddress_AddressLine20`, `u`.`BillingAddress_Tags0`, `u`.`BillingAddress_ZipCode0`, `u`.`BillingAddress_Country_Code0`, `u`.`BillingAddress_Country_FullName0`
+FROM (
+ SELECT `c`.`BillingAddress_AddressLine1`, `c`.`BillingAddress_AddressLine2`, `c`.`BillingAddress_Tags`, `c`.`BillingAddress_ZipCode`, `c`.`BillingAddress_Country_Code`, `c`.`BillingAddress_Country_FullName`, `c0`.`BillingAddress_AddressLine1` AS `BillingAddress_AddressLine10`, `c0`.`BillingAddress_AddressLine2` AS `BillingAddress_AddressLine20`, `c0`.`BillingAddress_Tags` AS `BillingAddress_Tags0`, `c0`.`BillingAddress_ZipCode` AS `BillingAddress_ZipCode0`, `c0`.`BillingAddress_Country_Code` AS `BillingAddress_Country_Code0`, `c0`.`BillingAddress_Country_FullName` AS `BillingAddress_Country_FullName0`
+ FROM `Customer` AS `c`
+ CROSS JOIN `Customer` AS `c0`
+ UNION
+ SELECT `c1`.`BillingAddress_AddressLine1`, `c1`.`BillingAddress_AddressLine2`, `c1`.`BillingAddress_Tags`, `c1`.`BillingAddress_ZipCode`, `c1`.`BillingAddress_Country_Code`, `c1`.`BillingAddress_Country_FullName`, `c2`.`BillingAddress_AddressLine1` AS `BillingAddress_AddressLine10`, `c2`.`BillingAddress_AddressLine2` AS `BillingAddress_AddressLine20`, `c2`.`BillingAddress_Tags` AS `BillingAddress_Tags0`, `c2`.`BillingAddress_ZipCode` AS `BillingAddress_ZipCode0`, `c2`.`BillingAddress_Country_Code` AS `BillingAddress_Country_Code0`, `c2`.`BillingAddress_Country_FullName` AS `BillingAddress_Country_FullName0`
+ FROM `Customer` AS `c1`
+ CROSS JOIN `Customer` AS `c2`
+) AS `u`
+ORDER BY `u`.`BillingAddress_ZipCode`, `u`.`BillingAddress_ZipCode0`
+LIMIT @__p_0
+""");
+ }
+
+ public override async Task Union_of_same_nested_complex_type_projected_twice_with_double_pushdown(bool async)
+ {
+ await base.Union_of_same_nested_complex_type_projected_twice_with_double_pushdown(async);
+
+ AssertSql(
+"""
+@__p_0='50'
+
+SELECT `u1`.`BillingAddress_AddressLine1`, `u1`.`BillingAddress_AddressLine2`, `u1`.`BillingAddress_Tags`, `u1`.`BillingAddress_ZipCode`, `u1`.`BillingAddress_Country_Code`, `u1`.`BillingAddress_Country_FullName`, `u1`.`BillingAddress_AddressLine10`, `u1`.`BillingAddress_AddressLine20`, `u1`.`BillingAddress_Tags0`, `u1`.`BillingAddress_ZipCode0`, `u1`.`BillingAddress_Country_Code0`, `u1`.`BillingAddress_Country_FullName0`
+FROM (
+ SELECT DISTINCT `u0`.`BillingAddress_AddressLine1`, `u0`.`BillingAddress_AddressLine2`, `u0`.`BillingAddress_Tags`, `u0`.`BillingAddress_ZipCode`, `u0`.`BillingAddress_Country_Code`, `u0`.`BillingAddress_Country_FullName`, `u0`.`BillingAddress_AddressLine10`, `u0`.`BillingAddress_AddressLine20`, `u0`.`BillingAddress_Tags0`, `u0`.`BillingAddress_ZipCode0`, `u0`.`BillingAddress_Country_Code0`, `u0`.`BillingAddress_Country_FullName0`
+ FROM (
+ SELECT `u`.`BillingAddress_AddressLine1`, `u`.`BillingAddress_AddressLine2`, `u`.`BillingAddress_Tags`, `u`.`BillingAddress_ZipCode`, `u`.`BillingAddress_Country_Code`, `u`.`BillingAddress_Country_FullName`, `u`.`BillingAddress_AddressLine10`, `u`.`BillingAddress_AddressLine20`, `u`.`BillingAddress_Tags0`, `u`.`BillingAddress_ZipCode0`, `u`.`BillingAddress_Country_Code0`, `u`.`BillingAddress_Country_FullName0`
+ FROM (
+ SELECT `c`.`BillingAddress_AddressLine1`, `c`.`BillingAddress_AddressLine2`, `c`.`BillingAddress_Tags`, `c`.`BillingAddress_ZipCode`, `c`.`BillingAddress_Country_Code`, `c`.`BillingAddress_Country_FullName`, `c0`.`BillingAddress_AddressLine1` AS `BillingAddress_AddressLine10`, `c0`.`BillingAddress_AddressLine2` AS `BillingAddress_AddressLine20`, `c0`.`BillingAddress_Tags` AS `BillingAddress_Tags0`, `c0`.`BillingAddress_ZipCode` AS `BillingAddress_ZipCode0`, `c0`.`BillingAddress_Country_Code` AS `BillingAddress_Country_Code0`, `c0`.`BillingAddress_Country_FullName` AS `BillingAddress_Country_FullName0`
+ FROM `Customer` AS `c`
+ CROSS JOIN `Customer` AS `c0`
+ UNION
+ SELECT `c1`.`BillingAddress_AddressLine1`, `c1`.`BillingAddress_AddressLine2`, `c1`.`BillingAddress_Tags`, `c1`.`BillingAddress_ZipCode`, `c1`.`BillingAddress_Country_Code`, `c1`.`BillingAddress_Country_FullName`, `c2`.`BillingAddress_AddressLine1` AS `BillingAddress_AddressLine10`, `c2`.`BillingAddress_AddressLine2` AS `BillingAddress_AddressLine20`, `c2`.`BillingAddress_Tags` AS `BillingAddress_Tags0`, `c2`.`BillingAddress_ZipCode` AS `BillingAddress_ZipCode0`, `c2`.`BillingAddress_Country_Code` AS `BillingAddress_Country_Code0`, `c2`.`BillingAddress_Country_FullName` AS `BillingAddress_Country_FullName0`
+ FROM `Customer` AS `c1`
+ CROSS JOIN `Customer` AS `c2`
+ ) AS `u`
+ ORDER BY `u`.`BillingAddress_ZipCode`, `u`.`BillingAddress_ZipCode0`
+ LIMIT @__p_0
+ ) AS `u0`
+) AS `u1`
+ORDER BY `u1`.`BillingAddress_ZipCode`, `u1`.`BillingAddress_ZipCode0`
+LIMIT @__p_0
+""");
+ }
+
+ public override async Task Same_entity_with_complex_type_projected_twice_with_pushdown_as_part_of_another_projection(bool async)
+ {
+ await base.Same_entity_with_complex_type_projected_twice_with_pushdown_as_part_of_another_projection(async);
+
+ AssertSql(
+"""
+SELECT `c`.`Id`, `s`.`Id`, `s`.`Name`, `s`.`BillingAddress_AddressLine1`, `s`.`BillingAddress_AddressLine2`, `s`.`BillingAddress_Tags`, `s`.`BillingAddress_ZipCode`, `s`.`BillingAddress_Country_Code`, `s`.`BillingAddress_Country_FullName`, `s`.`ShippingAddress_AddressLine1`, `s`.`ShippingAddress_AddressLine2`, `s`.`ShippingAddress_Tags`, `s`.`ShippingAddress_ZipCode`, `s`.`ShippingAddress_Country_Code`, `s`.`ShippingAddress_Country_FullName`, `s`.`Id0`, `s`.`Name0`, `s`.`BillingAddress_AddressLine10`, `s`.`BillingAddress_AddressLine20`, `s`.`BillingAddress_Tags0`, `s`.`BillingAddress_ZipCode0`, `s`.`BillingAddress_Country_Code0`, `s`.`BillingAddress_Country_FullName0`, `s`.`ShippingAddress_AddressLine10`, `s`.`ShippingAddress_AddressLine20`, `s`.`ShippingAddress_Tags0`, `s`.`ShippingAddress_ZipCode0`, `s`.`ShippingAddress_Country_Code0`, `s`.`ShippingAddress_Country_FullName0`, `s`.`c`
+FROM `Customer` AS `c`
+LEFT JOIN LATERAL (
+ SELECT `c0`.`Id`, `c0`.`Name`, `c0`.`BillingAddress_AddressLine1`, `c0`.`BillingAddress_AddressLine2`, `c0`.`BillingAddress_Tags`, `c0`.`BillingAddress_ZipCode`, `c0`.`BillingAddress_Country_Code`, `c0`.`BillingAddress_Country_FullName`, `c0`.`ShippingAddress_AddressLine1`, `c0`.`ShippingAddress_AddressLine2`, `c0`.`ShippingAddress_Tags`, `c0`.`ShippingAddress_ZipCode`, `c0`.`ShippingAddress_Country_Code`, `c0`.`ShippingAddress_Country_FullName`, `c1`.`Id` AS `Id0`, `c1`.`Name` AS `Name0`, `c1`.`BillingAddress_AddressLine1` AS `BillingAddress_AddressLine10`, `c1`.`BillingAddress_AddressLine2` AS `BillingAddress_AddressLine20`, `c1`.`BillingAddress_Tags` AS `BillingAddress_Tags0`, `c1`.`BillingAddress_ZipCode` AS `BillingAddress_ZipCode0`, `c1`.`BillingAddress_Country_Code` AS `BillingAddress_Country_Code0`, `c1`.`BillingAddress_Country_FullName` AS `BillingAddress_Country_FullName0`, `c1`.`ShippingAddress_AddressLine1` AS `ShippingAddress_AddressLine10`, `c1`.`ShippingAddress_AddressLine2` AS `ShippingAddress_AddressLine20`, `c1`.`ShippingAddress_Tags` AS `ShippingAddress_Tags0`, `c1`.`ShippingAddress_ZipCode` AS `ShippingAddress_ZipCode0`, `c1`.`ShippingAddress_Country_Code` AS `ShippingAddress_Country_Code0`, `c1`.`ShippingAddress_Country_FullName` AS `ShippingAddress_Country_FullName0`, 1 AS `c`
+ FROM `Customer` AS `c0`
+ CROSS JOIN `Customer` AS `c1`
+ ORDER BY `c0`.`Id`, `c1`.`Id` DESC
+ LIMIT 1
+) AS `s` ON TRUE
+""");
+ }
+
+ public override async Task Same_complex_type_projected_twice_with_pushdown_as_part_of_another_projection(bool async)
+ {
+ await base.Same_complex_type_projected_twice_with_pushdown_as_part_of_another_projection(async);
+
+ AssertSql();
+ }
+
+ public override async Task GroupBy_over_property_in_nested_complex_type(bool async)
+ {
+ await base.GroupBy_over_property_in_nested_complex_type(async);
+
+ AssertSql(
+"""
+SELECT `c`.`ShippingAddress_Country_Code` AS `Code`, COUNT(*) AS `Count`
+FROM `Customer` AS `c`
+GROUP BY `c`.`ShippingAddress_Country_Code`
+""");
+ }
+
+ public override async Task GroupBy_over_complex_type(bool async)
+ {
+ await base.GroupBy_over_complex_type(async);
+
+ AssertSql(
+"""
+SELECT `c`.`ShippingAddress_AddressLine1`, `c`.`ShippingAddress_AddressLine2`, `c`.`ShippingAddress_Tags`, `c`.`ShippingAddress_ZipCode`, `c`.`ShippingAddress_Country_Code`, `c`.`ShippingAddress_Country_FullName`, COUNT(*) AS `Count`
+FROM `Customer` AS `c`
+GROUP BY `c`.`ShippingAddress_AddressLine1`, `c`.`ShippingAddress_AddressLine2`, `c`.`ShippingAddress_Tags`, `c`.`ShippingAddress_ZipCode`, `c`.`ShippingAddress_Country_Code`, `c`.`ShippingAddress_Country_FullName`
+""");
+ }
+
+ public override async Task GroupBy_over_nested_complex_type(bool async)
+ {
+ await base.GroupBy_over_nested_complex_type(async);
+
+ AssertSql(
+"""
+SELECT `c`.`ShippingAddress_Country_Code`, `c`.`ShippingAddress_Country_FullName`, COUNT(*) AS `Count`
+FROM `Customer` AS `c`
+GROUP BY `c`.`ShippingAddress_Country_Code`, `c`.`ShippingAddress_Country_FullName`
+""");
+ }
+
+ public override async Task Entity_with_complex_type_with_group_by_and_first(bool async)
+ {
+ await base.Entity_with_complex_type_with_group_by_and_first(async);
+
+ AssertSql(
+"""
+SELECT `c3`.`Id`, `c3`.`Name`, `c3`.`BillingAddress_AddressLine1`, `c3`.`BillingAddress_AddressLine2`, `c3`.`BillingAddress_Tags`, `c3`.`BillingAddress_ZipCode`, `c3`.`BillingAddress_Country_Code`, `c3`.`BillingAddress_Country_FullName`, `c3`.`ShippingAddress_AddressLine1`, `c3`.`ShippingAddress_AddressLine2`, `c3`.`ShippingAddress_Tags`, `c3`.`ShippingAddress_ZipCode`, `c3`.`ShippingAddress_Country_Code`, `c3`.`ShippingAddress_Country_FullName`
+FROM (
+ SELECT `c`.`Id`
+ FROM `Customer` AS `c`
+ GROUP BY `c`.`Id`
+) AS `c1`
+LEFT JOIN (
+ SELECT `c2`.`Id`, `c2`.`Name`, `c2`.`BillingAddress_AddressLine1`, `c2`.`BillingAddress_AddressLine2`, `c2`.`BillingAddress_Tags`, `c2`.`BillingAddress_ZipCode`, `c2`.`BillingAddress_Country_Code`, `c2`.`BillingAddress_Country_FullName`, `c2`.`ShippingAddress_AddressLine1`, `c2`.`ShippingAddress_AddressLine2`, `c2`.`ShippingAddress_Tags`, `c2`.`ShippingAddress_ZipCode`, `c2`.`ShippingAddress_Country_Code`, `c2`.`ShippingAddress_Country_FullName`
+ FROM (
+ SELECT `c0`.`Id`, `c0`.`Name`, `c0`.`BillingAddress_AddressLine1`, `c0`.`BillingAddress_AddressLine2`, `c0`.`BillingAddress_Tags`, `c0`.`BillingAddress_ZipCode`, `c0`.`BillingAddress_Country_Code`, `c0`.`BillingAddress_Country_FullName`, `c0`.`ShippingAddress_AddressLine1`, `c0`.`ShippingAddress_AddressLine2`, `c0`.`ShippingAddress_Tags`, `c0`.`ShippingAddress_ZipCode`, `c0`.`ShippingAddress_Country_Code`, `c0`.`ShippingAddress_Country_FullName`, ROW_NUMBER() OVER(PARTITION BY `c0`.`Id` ORDER BY `c0`.`Id`) AS `row`
+ FROM `Customer` AS `c0`
+ ) AS `c2`
+ WHERE `c2`.`row` <= 1
+) AS `c3` ON `c1`.`Id` = `c3`.`Id`
+""");
+ }
+
+ public override async Task Projecting_property_of_complex_type_using_left_join_with_pushdown(bool async)
+ {
+ await base.Projecting_property_of_complex_type_using_left_join_with_pushdown(async);
+
+ AssertSql(
+"""
+SELECT `c1`.`BillingAddress_ZipCode`
+FROM `CustomerGroup` AS `c`
+LEFT JOIN (
+ SELECT `c0`.`Id`, `c0`.`BillingAddress_ZipCode`
+ FROM `Customer` AS `c0`
+ WHERE `c0`.`Id` > 5
+) AS `c1` ON `c`.`Id` = `c1`.`Id`
+""");
+ }
+
+ public override async Task Projecting_complex_from_optional_navigation_using_conditional(bool async)
+ {
+ await base.Projecting_complex_from_optional_navigation_using_conditional(async);
+
+ AssertSql(
+"""
+@__p_0='20'
+
+SELECT `s0`.`ShippingAddress_ZipCode`
+FROM (
+ SELECT DISTINCT `s`.`ShippingAddress_AddressLine1`, `s`.`ShippingAddress_AddressLine2`, `s`.`ShippingAddress_Tags`, `s`.`ShippingAddress_ZipCode`, `s`.`ShippingAddress_Country_Code`, `s`.`ShippingAddress_Country_FullName`
+ FROM (
+ SELECT `c0`.`ShippingAddress_AddressLine1`, `c0`.`ShippingAddress_AddressLine2`, `c0`.`ShippingAddress_Tags`, `c0`.`ShippingAddress_ZipCode`, `c0`.`ShippingAddress_Country_Code`, `c0`.`ShippingAddress_Country_FullName`
+ FROM `CustomerGroup` AS `c`
+ LEFT JOIN `Customer` AS `c0` ON `c`.`OptionalCustomerId` = `c0`.`Id`
+ ORDER BY `c0`.`ShippingAddress_ZipCode`
+ LIMIT @__p_0
+ ) AS `s`
+) AS `s0`
+""");
+ }
+
+ public override async Task Project_entity_with_complex_type_pushdown_and_then_left_join(bool async)
+ {
+ await base.Project_entity_with_complex_type_pushdown_and_then_left_join(async);
+
+ AssertSql(
+"""
+@__p_0='20'
+@__p_1='30'
+
+SELECT `c3`.`BillingAddress_ZipCode` AS `Zip1`, `c4`.`ShippingAddress_ZipCode` AS `Zip2`
+FROM (
+ SELECT DISTINCT `c0`.`Id`, `c0`.`Name`, `c0`.`BillingAddress_AddressLine1`, `c0`.`BillingAddress_AddressLine2`, `c0`.`BillingAddress_Tags`, `c0`.`BillingAddress_ZipCode`, `c0`.`BillingAddress_Country_Code`, `c0`.`BillingAddress_Country_FullName`, `c0`.`ShippingAddress_AddressLine1`, `c0`.`ShippingAddress_AddressLine2`, `c0`.`ShippingAddress_Tags`, `c0`.`ShippingAddress_ZipCode`, `c0`.`ShippingAddress_Country_Code`, `c0`.`ShippingAddress_Country_FullName`
+ FROM (
+ SELECT `c`.`Id`, `c`.`Name`, `c`.`BillingAddress_AddressLine1`, `c`.`BillingAddress_AddressLine2`, `c`.`BillingAddress_Tags`, `c`.`BillingAddress_ZipCode`, `c`.`BillingAddress_Country_Code`, `c`.`BillingAddress_Country_FullName`, `c`.`ShippingAddress_AddressLine1`, `c`.`ShippingAddress_AddressLine2`, `c`.`ShippingAddress_Tags`, `c`.`ShippingAddress_ZipCode`, `c`.`ShippingAddress_Country_Code`, `c`.`ShippingAddress_Country_FullName`
+ FROM `Customer` AS `c`
+ ORDER BY `c`.`Id`
+ LIMIT @__p_0
+ ) AS `c0`
+) AS `c3`
+LEFT JOIN (
+ SELECT DISTINCT `c2`.`Id`, `c2`.`Name`, `c2`.`BillingAddress_AddressLine1`, `c2`.`BillingAddress_AddressLine2`, `c2`.`BillingAddress_Tags`, `c2`.`BillingAddress_ZipCode`, `c2`.`BillingAddress_Country_Code`, `c2`.`BillingAddress_Country_FullName`, `c2`.`ShippingAddress_AddressLine1`, `c2`.`ShippingAddress_AddressLine2`, `c2`.`ShippingAddress_Tags`, `c2`.`ShippingAddress_ZipCode`, `c2`.`ShippingAddress_Country_Code`, `c2`.`ShippingAddress_Country_FullName`
+ FROM (
+ SELECT `c1`.`Id`, `c1`.`Name`, `c1`.`BillingAddress_AddressLine1`, `c1`.`BillingAddress_AddressLine2`, `c1`.`BillingAddress_Tags`, `c1`.`BillingAddress_ZipCode`, `c1`.`BillingAddress_Country_Code`, `c1`.`BillingAddress_Country_FullName`, `c1`.`ShippingAddress_AddressLine1`, `c1`.`ShippingAddress_AddressLine2`, `c1`.`ShippingAddress_Tags`, `c1`.`ShippingAddress_ZipCode`, `c1`.`ShippingAddress_Country_Code`, `c1`.`ShippingAddress_Country_FullName`
+ FROM `Customer` AS `c1`
+ ORDER BY `c1`.`Id` DESC
+ LIMIT @__p_1
+ ) AS `c2`
+) AS `c4` ON `c3`.`Id` = `c4`.`Id`
+""");
+ }
+
[ConditionalFact]
public virtual void Check_all_tests_overridden()
- => TestHelpers.AssertAllMethodsOverridden(GetType());
+ => MySqlTestHelpers.AssertAllMethodsOverridden(GetType());
private void AssertSql(params string[] expected)
=> Fixture.TestSqlLoggerFactory.AssertBaseline(expected);
diff --git a/test/EFCore.MySql.FunctionalTests/Query/CompositeKeysQueryMySqlTest.cs b/test/EFCore.MySql.FunctionalTests/Query/CompositeKeysQueryMySqlTest.cs
index 5bfbf4660..0a99234cc 100644
--- a/test/EFCore.MySql.FunctionalTests/Query/CompositeKeysQueryMySqlTest.cs
+++ b/test/EFCore.MySql.FunctionalTests/Query/CompositeKeysQueryMySqlTest.cs
@@ -15,9 +15,6 @@ public CompositeKeysQueryMySqlTest(
//Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper);
}
- protected override bool CanExecuteQueryString
- => true;
-
public override async Task Projecting_multiple_collections_same_level_top_level_ordering(bool async)
{
await base.Projecting_multiple_collections_same_level_top_level_ordering(async);
diff --git a/test/EFCore.MySql.FunctionalTests/Query/CompositeKeysSplitQueryMySqlTest.cs b/test/EFCore.MySql.FunctionalTests/Query/CompositeKeysSplitQueryMySqlTest.cs
index 504d921ed..ad90f7426 100644
--- a/test/EFCore.MySql.FunctionalTests/Query/CompositeKeysSplitQueryMySqlTest.cs
+++ b/test/EFCore.MySql.FunctionalTests/Query/CompositeKeysSplitQueryMySqlTest.cs
@@ -15,9 +15,6 @@ public CompositeKeysSplitQueryMySqlTest(
//Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper);
}
- protected override bool CanExecuteQueryString
- => true;
-
public override async Task Projecting_collections_multi_level(bool async)
{
await base.Projecting_collections_multi_level(async);
diff --git a/test/EFCore.MySql.FunctionalTests/Query/DateOnlyQueryMySqlTest.cs b/test/EFCore.MySql.FunctionalTests/Query/DateOnlyQueryMySqlTest.cs
index a0f957dd2..43937b2e5 100644
--- a/test/EFCore.MySql.FunctionalTests/Query/DateOnlyQueryMySqlTest.cs
+++ b/test/EFCore.MySql.FunctionalTests/Query/DateOnlyQueryMySqlTest.cs
@@ -183,7 +183,7 @@ protected DateOnlyQueryMySqlTestBase(TFixture fixture)
protected virtual DbContext CreateContext() => Fixture.CreateContext();
- public abstract class DateOnlyQueryMySqlFixtureBase : SharedStoreFixtureBase, IQueryFixtureBase
+ public abstract class DateOnlyQueryMySqlFixtureBase : SharedStoreFixtureBase, IQueryFixtureBase, ITestSqlLoggerFactory
{
protected override string StoreName => "DateOnlyQueryTest";
public TestSqlLoggerFactory TestSqlLoggerFactory => (TestSqlLoggerFactory)ListLoggerFactory;
diff --git a/test/EFCore.MySql.FunctionalTests/Query/Ef6GroupByMySqlTest.cs b/test/EFCore.MySql.FunctionalTests/Query/Ef6GroupByMySqlTest.cs
index cbb747070..e18c57229 100644
--- a/test/EFCore.MySql.FunctionalTests/Query/Ef6GroupByMySqlTest.cs
+++ b/test/EFCore.MySql.FunctionalTests/Query/Ef6GroupByMySqlTest.cs
@@ -20,7 +20,7 @@ public Ef6GroupByMySqlTest(Ef6GroupByMySqlFixture fixture, ITestOutputHelper tes
[ConditionalFact]
public virtual void Check_all_tests_overridden()
- => TestHelpers.AssertAllMethodsOverridden(GetType());
+ => MySqlTestHelpers.AssertAllMethodsOverridden(GetType());
public override async Task GroupBy_is_optimized_when_projecting_group_key(bool async)
{
@@ -165,12 +165,10 @@ public override async Task GroupBy_is_optimized_when_projecting_conditional_expr
AssertSql(
"""
-@__p_0='False'
-
SELECT CASE
WHEN `a`.`FirstName` IS NULL THEN 'is null'
ELSE 'not null'
-END AS `keyIsNull`, @__p_0 AS `logicExpression`
+END AS `keyIsNull`, FALSE AS `logicExpression`
FROM `ArubaOwner` AS `a`
GROUP BY `a`.`FirstName`
""");
@@ -186,10 +184,10 @@ GROUP BY `a`.`FirstName`
// ) AS [Distinct1]";
}
- public override async Task GroupBy_is_optimized_when_filerting_and_projecting_anonymous_type_with_group_key_and_function_aggregate(
+ public override async Task GroupBy_is_optimized_when_filtering_and_projecting_anonymous_type_with_group_key_and_function_aggregate(
bool async)
{
- await base.GroupBy_is_optimized_when_filerting_and_projecting_anonymous_type_with_group_key_and_function_aggregate(async);
+ await base.GroupBy_is_optimized_when_filtering_and_projecting_anonymous_type_with_group_key_and_function_aggregate(async);
AssertSql(
$"""
@@ -489,12 +487,11 @@ public override async Task Group_Join_from_LINQ_101(bool async)
"""
SELECT `c`.`Id`, `c`.`CompanyName`, `c`.`Region`, `s`.`Id`, `s`.`CustomerId`, `s`.`OrderDate`, `s`.`Total`, `s`.`Id0`
FROM `CustomerForLinq` AS `c`
-LEFT JOIN LATERAL (
+LEFT JOIN (
SELECT `o`.`Id`, `o`.`CustomerId`, `o`.`OrderDate`, `o`.`Total`, `c0`.`Id` AS `Id0`
FROM `OrderForLinq` AS `o`
LEFT JOIN `CustomerForLinq` AS `c0` ON `o`.`CustomerId` = `c0`.`Id`
- WHERE `c`.`Id` = `c0`.`Id`
-) AS `s` ON TRUE
+) AS `s` ON `c`.`Id` = `s`.`Id0`
ORDER BY `c`.`Id`, `s`.`Id`
""");
}
@@ -912,7 +909,7 @@ LEFT JOIN (
private void AssertSql(params string[] expected)
=> Fixture.TestSqlLoggerFactory.AssertBaseline(expected);
- public class Ef6GroupByMySqlFixture : Ef6GroupByFixtureBase
+ public class Ef6GroupByMySqlFixture : Ef6GroupByFixtureBase, ITestSqlLoggerFactory
{
public TestSqlLoggerFactory TestSqlLoggerFactory
=> (TestSqlLoggerFactory)ListLoggerFactory;
diff --git a/test/EFCore.MySql.FunctionalTests/Query/EntitySplittingQueryMySqlTest.cs b/test/EFCore.MySql.FunctionalTests/Query/EntitySplittingQueryMySqlTest.cs
index 4240725e0..d1456a8ab 100644
--- a/test/EFCore.MySql.FunctionalTests/Query/EntitySplittingQueryMySqlTest.cs
+++ b/test/EFCore.MySql.FunctionalTests/Query/EntitySplittingQueryMySqlTest.cs
@@ -16,7 +16,7 @@ public EntitySplittingQueryMySqlTest(ITestOutputHelper testOutputHelper)
[ConditionalFact]
public virtual void Check_all_tests_overridden()
- => TestHelpers.AssertAllMethodsOverridden(GetType());
+ => MySqlTestHelpers.AssertAllMethodsOverridden(GetType());
public override async Task Can_query_entity_which_is_split_in_two(bool async)
{
diff --git a/test/EFCore.MySql.FunctionalTests/Query/EscapesMySqlNoBackslashesTest.cs b/test/EFCore.MySql.FunctionalTests/Query/EscapesMySqlNoBackslashesTest.cs
index 691a5af80..461cdc759 100644
--- a/test/EFCore.MySql.FunctionalTests/Query/EscapesMySqlNoBackslashesTest.cs
+++ b/test/EFCore.MySql.FunctionalTests/Query/EscapesMySqlNoBackslashesTest.cs
@@ -17,9 +17,9 @@ public EscapesMySqlNoBackslashesTest(EscapesMySqlNoBackslashesFixture fixture, I
}
[ConditionalFact]
- public override void Input_query_escapes_parameter()
+ public override async Task Input_query_escapes_parameter()
{
- base.Input_query_escapes_parameter();
+ await base.Input_query_escapes_parameter();
if (AppConfig.ServerVersion.Supports.Returning)
{
diff --git a/test/EFCore.MySql.FunctionalTests/Query/EscapesMySqlTest.cs b/test/EFCore.MySql.FunctionalTests/Query/EscapesMySqlTest.cs
index ab121f014..d58d53b60 100644
--- a/test/EFCore.MySql.FunctionalTests/Query/EscapesMySqlTest.cs
+++ b/test/EFCore.MySql.FunctionalTests/Query/EscapesMySqlTest.cs
@@ -16,9 +16,9 @@ public EscapesMySqlTest(EscapesMySqlFixture fixture, ITestOutputHelper testOutpu
}
[ConditionalFact]
- public override void Input_query_escapes_parameter()
+ public override async Task Input_query_escapes_parameter()
{
- base.Input_query_escapes_parameter();
+ await base.Input_query_escapes_parameter();
if (AppConfig.ServerVersion.Supports.Returning)
{
diff --git a/test/EFCore.MySql.FunctionalTests/Query/EscapesMySqlTestBase.cs b/test/EFCore.MySql.FunctionalTests/Query/EscapesMySqlTestBase.cs
index 53a2ce84b..ac08eae8d 100644
--- a/test/EFCore.MySql.FunctionalTests/Query/EscapesMySqlTestBase.cs
+++ b/test/EFCore.MySql.FunctionalTests/Query/EscapesMySqlTestBase.cs
@@ -24,21 +24,21 @@ protected EscapesMySqlTestBase(TFixture fixture)
}
[ConditionalFact]
- public virtual void Input_query_escapes_parameter()
+ public virtual async Task Input_query_escapes_parameter()
{
- ExecuteWithStrategyInTransaction(
- context =>
+ await ExecuteWithStrategyInTransactionAsync(
+ async context =>
{
context.Artists.Add(new Artist
{
Name = @"Back\slash's Garden Party",
});
- context.SaveChanges();
+ await context.SaveChangesAsync();
},
- context =>
+ async context =>
{
- var artists = context.Artists.Where(x => x.Name.EndsWith(" Garden Party")).ToList();
+ var artists = await context.Artists.Where(x => x.Name.EndsWith(" Garden Party")).ToListAsync();
Assert.Single(artists);
Assert.True(artists[0].Name == @"Back\slash's Garden Party");
});
@@ -108,18 +108,16 @@ public virtual async Task Where_contains_query_escapes(bool async)
protected void AssertSql(params string[] expected)
=> Fixture.TestSqlLoggerFactory.AssertBaseline(expected);
- protected virtual void ExecuteWithStrategyInTransaction(
- Action testOperation,
- Action nestedTestOperation1 = null,
- Action nestedTestOperation2 = null)
- {
- TestHelpers.ExecuteWithStrategyInTransaction(
+ protected virtual Task ExecuteWithStrategyInTransactionAsync(
+ Func testOperation,
+ Func nestedTestOperation1 = null,
+ Func nestedTestOperation2 = null)
+ => TestHelpers.ExecuteWithStrategyInTransactionAsync(
CreateContext,
UseTransaction,
testOperation,
nestedTestOperation1,
nestedTestOperation2);
- }
protected virtual void UseTransaction(DatabaseFacade facade, IDbContextTransaction transaction)
=> facade.UseTransaction(transaction.GetDbTransaction());
@@ -137,7 +135,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con
MySqlTestHelpers.Instance.EnsureSufficientKeySpace(modelBuilder.Model, TestStore);
}
- protected override void Seed(MusicStoreContext context)
+ protected override async Task SeedAsync(MusicStoreContext context)
{
context.Artists.AddRange(
new Artist { ArtistId = 1, Name = @"Back\slasher's" },
@@ -145,7 +143,7 @@ protected override void Seed(MusicStoreContext context)
new Artist { ArtistId = 3, Name = @"John's Chill Box" }
);
- context.SaveChanges();
+ await context.SaveChangesAsync();
}
}
}
diff --git a/test/EFCore.MySql.FunctionalTests/Query/FunkyDataQueryMySqlTest.cs b/test/EFCore.MySql.FunctionalTests/Query/FunkyDataQueryMySqlTest.cs
index bc3fd0740..0cfb7c607 100644
--- a/test/EFCore.MySql.FunctionalTests/Query/FunkyDataQueryMySqlTest.cs
+++ b/test/EFCore.MySql.FunctionalTests/Query/FunkyDataQueryMySqlTest.cs
@@ -195,7 +195,7 @@ protected override void ClearLog()
private void AssertSql(params string[] expected)
=> Fixture.TestSqlLoggerFactory.AssertBaseline(expected);
- public class FunkyDataQueryMySqlFixture : FunkyDataQueryFixtureBase
+ public class FunkyDataQueryMySqlFixture : FunkyDataQueryFixtureBase, ITestSqlLoggerFactory
{
public TestSqlLoggerFactory TestSqlLoggerFactory
=> (TestSqlLoggerFactory)ServiceProvider.GetRequiredService();
diff --git a/test/EFCore.MySql.FunctionalTests/Query/GearsOfWarQueryMySqlTest.MySql.cs b/test/EFCore.MySql.FunctionalTests/Query/GearsOfWarQueryMySqlTest.MySql.cs
index 7a537a2cc..1aae4b327 100644
--- a/test/EFCore.MySql.FunctionalTests/Query/GearsOfWarQueryMySqlTest.MySql.cs
+++ b/test/EFCore.MySql.FunctionalTests/Query/GearsOfWarQueryMySqlTest.MySql.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
@@ -206,9 +206,9 @@ await AssertQuery(
ss => ss.Set().Where(e => e.Timeline == e.Timeline.DateTime),
ss => ss.Set().Where(e => true));
- AssertSql(
- """
-SELECT `m`.`Id`, `m`.`CodeName`, `m`.`Date`, `m`.`Duration`, `m`.`Rating`, `m`.`Time`, `m`.`Timeline`
+ AssertSql(
+"""
+SELECT `m`.`Id`, `m`.`CodeName`, `m`.`Date`, `m`.`Difficulty`, `m`.`Duration`, `m`.`Rating`, `m`.`Time`, `m`.`Timeline`
FROM `Missions` AS `m`
WHERE `m`.`Timeline` = `m`.`Timeline`
""");
@@ -223,9 +223,9 @@ await AssertQuery(
ss => ss.Set().Where(e => e.Timeline == e.Timeline.UtcDateTime),
ss => ss.Set().Where(e => true));
- AssertSql(
- """
-SELECT `m`.`Id`, `m`.`CodeName`, `m`.`Date`, `m`.`Duration`, `m`.`Rating`, `m`.`Time`, `m`.`Timeline`
+ AssertSql(
+"""
+SELECT `m`.`Id`, `m`.`CodeName`, `m`.`Date`, `m`.`Difficulty`, `m`.`Duration`, `m`.`Rating`, `m`.`Time`, `m`.`Timeline`
FROM `Missions` AS `m`
WHERE `m`.`Timeline` = `m`.`Timeline`
""");
diff --git a/test/EFCore.MySql.FunctionalTests/Query/GearsOfWarQueryMySqlTest.cs b/test/EFCore.MySql.FunctionalTests/Query/GearsOfWarQueryMySqlTest.cs
index 61da8c51d..081add88c 100644
--- a/test/EFCore.MySql.FunctionalTests/Query/GearsOfWarQueryMySqlTest.cs
+++ b/test/EFCore.MySql.FunctionalTests/Query/GearsOfWarQueryMySqlTest.cs
@@ -21,9 +21,6 @@ public GearsOfWarQueryMySqlTest(GearsOfWarQueryMySqlFixture fixture, ITestOutput
//Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper);
}
- protected override bool CanExecuteQueryString
- => true;
-
public override Task DateTimeOffset_Contains_Less_than_Greater_than(bool async)
{
var dto = MySqlTestHelpers.GetExpectedValue(new DateTimeOffset(599898024001234567, new TimeSpan(1, 30, 0)));
@@ -313,10 +310,13 @@ public override async Task Group_by_with_having_StartsWith_with_null_parameter_a
AssertSql(
"""
-SELECT `g`.`FullName`
-FROM `Gears` AS `g`
-GROUP BY `g`.`FullName`
-HAVING FALSE
+SELECT `g0`.`FullName`
+FROM (
+ SELECT `g`.`FullName`, FALSE AS `c`
+ FROM `Gears` AS `g`
+ GROUP BY `g`.`FullName`, `c`
+ HAVING `c`
+) AS `g0`
""");
}
@@ -347,12 +347,19 @@ await AssertQuery(
where m.Timeline.Hour == /* 10 */ 8
select m);
- AssertSql(
+ AssertSql(
"""
-SELECT `m`.`Id`, `m`.`CodeName`, `m`.`Date`, `m`.`Duration`, `m`.`Rating`, `m`.`Time`, `m`.`Timeline`
+SELECT `m`.`Id`, `m`.`CodeName`, `m`.`Date`, `m`.`Difficulty`, `m`.`Duration`, `m`.`Rating`, `m`.`Time`, `m`.`Timeline`
FROM `Missions` AS `m`
WHERE EXTRACT(hour FROM `m`.`Timeline`) = 8
""");
}
+
+ // TODO: Implement once TimeSpan is translated as ticks instead of TIME.
+ public override async Task Non_string_concat_uses_appropriate_type_mapping(bool async)
+ {
+ var exception = await Assert.ThrowsAsync(() => base.Non_string_concat_uses_appropriate_type_mapping(async));
+ Assert.Equal("Unable to cast object of type 'System.Decimal' to type 'System.TimeSpan'.", exception.Message);
+ }
}
}
diff --git a/test/EFCore.MySql.FunctionalTests/Query/JsonMicrosoftDomChangeTrackingTest.cs b/test/EFCore.MySql.FunctionalTests/Query/JsonMicrosoftDomChangeTrackingTest.cs
index 07a706f79..b6e928807 100644
--- a/test/EFCore.MySql.FunctionalTests/Query/JsonMicrosoftDomChangeTrackingTest.cs
+++ b/test/EFCore.MySql.FunctionalTests/Query/JsonMicrosoftDomChangeTrackingTest.cs
@@ -1,5 +1,6 @@
using System.Linq;
using System.Text.Json;
+using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.TestUtilities;
@@ -226,7 +227,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
}
}
- public void Seed()
+ public async Task SeedAsync()
{
var (customer1, customer2, customer3) = (CreateCustomer1(), CreateCustomer2(), CreateCustomer3());
@@ -234,7 +235,7 @@ public void Seed()
new JsonEntity { Id = 1, CustomerDocument = customer1, CustomerElement = customer1.RootElement },
new JsonEntity { Id = 2, CustomerDocument = customer2, CustomerElement = customer2.RootElement },
new JsonEntity { Id = 3, CustomerDocument = customer3, CustomerElement = customer3.RootElement });
- SaveChanges();
+ await SaveChangesAsync();
}
public static JsonDocument CreateCustomer1()
@@ -324,7 +325,7 @@ public class JsonMicrosoftDomChangeTrackingFixture : ServiceProviderPerContextFi
{
protected override string StoreName => "JsonMicrosoftDomChangeTrackingTest";
protected override ITestStoreFactory TestStoreFactory => MySqlTestStoreFactory.Instance;
- protected override void Seed(JsonMicrosoftDomChangeTrackingContext context) => context.Seed();
+ protected override Task SeedAsync(JsonMicrosoftDomChangeTrackingContext context) => context.SeedAsync();
protected override IServiceCollection AddServices(IServiceCollection serviceCollection)
{
diff --git a/test/EFCore.MySql.FunctionalTests/Query/JsonMicrosoftDomQueryTest.cs b/test/EFCore.MySql.FunctionalTests/Query/JsonMicrosoftDomQueryTest.cs
index 22c4d38bf..b1cf25b17 100644
--- a/test/EFCore.MySql.FunctionalTests/Query/JsonMicrosoftDomQueryTest.cs
+++ b/test/EFCore.MySql.FunctionalTests/Query/JsonMicrosoftDomQueryTest.cs
@@ -1,6 +1,7 @@
using System;
using System.Linq;
using System.Text.Json;
+using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.TestUtilities;
@@ -476,7 +477,7 @@ public class JsonDomQueryContext : PoolableDbContext
public JsonDomQueryContext(DbContextOptions options) : base(options) {}
- public static void Seed(JsonDomQueryContext context)
+ public static async Task SeedAsync(JsonDomQueryContext context)
{
var (customer1, customer2, customer3) = (createCustomer1(), createCustomer2(), createCustomer3());
@@ -484,7 +485,7 @@ public static void Seed(JsonDomQueryContext context)
new JsonEntity { Id = 1, CustomerDocument = customer1, CustomerElement = customer1.RootElement },
new JsonEntity { Id = 2, CustomerDocument = customer2, CustomerElement = customer2.RootElement },
new JsonEntity { Id = 3, CustomerDocument = customer3, CustomerElement = customer3.RootElement });
- context.SaveChanges();
+ await context.SaveChangesAsync();
static JsonDocument createCustomer1() => JsonDocument.Parse(@"
{
@@ -564,7 +565,7 @@ public class JsonMicrosoftDomQueryFixture : SharedStoreFixtureBase "JsonMicrosoftDomQueryTest";
protected override ITestStoreFactory TestStoreFactory => MySqlTestStoreFactory.Instance;
public TestSqlLoggerFactory TestSqlLoggerFactory => (TestSqlLoggerFactory)ListLoggerFactory;
- protected override void Seed(JsonDomQueryContext context) => JsonDomQueryContext.Seed(context);
+ protected override Task SeedAsync(JsonDomQueryContext context) => JsonDomQueryContext.SeedAsync(context);
protected override IServiceCollection AddServices(IServiceCollection serviceCollection)
{
diff --git a/test/EFCore.MySql.FunctionalTests/Query/JsonNewtonsoftDomChangeTrackingTest.cs b/test/EFCore.MySql.FunctionalTests/Query/JsonNewtonsoftDomChangeTrackingTest.cs
index d5bcf80a0..ea8fff5c8 100644
--- a/test/EFCore.MySql.FunctionalTests/Query/JsonNewtonsoftDomChangeTrackingTest.cs
+++ b/test/EFCore.MySql.FunctionalTests/Query/JsonNewtonsoftDomChangeTrackingTest.cs
@@ -1,4 +1,5 @@
using System.Linq;
+using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.TestUtilities;
@@ -226,7 +227,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
}
}
- public void Seed()
+ public async Task SeedAsync()
{
var (customer1, customer2, customer3) = (CreateCustomer1(), CreateCustomer2(), CreateCustomer3());
@@ -234,7 +235,7 @@ public void Seed()
new JsonEntity { Id = 1, CustomerJObject = customer1, CustomerJToken = customer1},
new JsonEntity { Id = 2, CustomerJObject = customer2, CustomerJToken = customer2},
new JsonEntity { Id = 3, CustomerJObject = null, CustomerJToken = customer3});
- SaveChanges();
+ await SaveChangesAsync();
}
public static JObject CreateCustomer1()
@@ -324,7 +325,7 @@ public class JsonMicrosoftDomChangeTrackingFixture : ServiceProviderPerContextFi
{
protected override string StoreName => "JsonNewtonsoftDomChangeTrackingTest";
protected override ITestStoreFactory TestStoreFactory => MySqlTestStoreFactory.Instance;
- protected override void Seed(JsonMicrosoftDomChangeTrackingContext context) => context.Seed();
+ protected override Task SeedAsync(JsonMicrosoftDomChangeTrackingContext context) => context.SeedAsync();
protected override IServiceCollection AddServices(IServiceCollection serviceCollection)
{
diff --git a/test/EFCore.MySql.FunctionalTests/Query/JsonNewtonsoftDomQueryTest.cs b/test/EFCore.MySql.FunctionalTests/Query/JsonNewtonsoftDomQueryTest.cs
index 570f0473c..c5ae525c8 100644
--- a/test/EFCore.MySql.FunctionalTests/Query/JsonNewtonsoftDomQueryTest.cs
+++ b/test/EFCore.MySql.FunctionalTests/Query/JsonNewtonsoftDomQueryTest.cs
@@ -1,5 +1,6 @@
using System;
using System.Linq;
+using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.TestUtilities;
@@ -504,7 +505,7 @@ public class JsonDomQueryContext : PoolableDbContext
public JsonDomQueryContext(DbContextOptions options) : base(options) {}
- public static void Seed(JsonDomQueryContext context)
+ public static async Task SeedAsync(JsonDomQueryContext context)
{
var (customer1, customer2, customer3) = (createCustomer1(), createCustomer2(), createCustomer3());
@@ -512,7 +513,7 @@ public static void Seed(JsonDomQueryContext context)
new JsonEntity { Id = 1, CustomerJObject = customer1, CustomerJToken = customer1},
new JsonEntity { Id = 2, CustomerJObject = customer2, CustomerJToken = customer2},
new JsonEntity { Id = 3, CustomerJObject = null, CustomerJToken = customer3});
- context.SaveChanges();
+ await context.SaveChangesAsync();
static JObject createCustomer1() => JObject.Parse(@"
{
@@ -592,7 +593,7 @@ public class JsonNewtonsoftDomQueryFixture : SharedStoreFixtureBase "JsonNewtonsoftDomQueryTest";
protected override ITestStoreFactory TestStoreFactory => MySqlTestStoreFactory.Instance;
public TestSqlLoggerFactory TestSqlLoggerFactory => (TestSqlLoggerFactory)ListLoggerFactory;
- protected override void Seed(JsonDomQueryContext context) => JsonDomQueryContext.Seed(context);
+ protected override Task SeedAsync(JsonDomQueryContext context) => JsonDomQueryContext.SeedAsync(context);
protected override IServiceCollection AddServices(IServiceCollection serviceCollection)
{
diff --git a/test/EFCore.MySql.FunctionalTests/Query/JsonPocoChangeTrackingTestBase.cs b/test/EFCore.MySql.FunctionalTests/Query/JsonPocoChangeTrackingTestBase.cs
index 7cd5f8069..e55192a18 100644
--- a/test/EFCore.MySql.FunctionalTests/Query/JsonPocoChangeTrackingTestBase.cs
+++ b/test/EFCore.MySql.FunctionalTests/Query/JsonPocoChangeTrackingTestBase.cs
@@ -1,6 +1,7 @@
using System;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
+using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.TestUtilities;
@@ -248,12 +249,12 @@ private void SetGlobalJsonChangeTrackingOptions(
addOrUpdateExtensionMethod.Invoke(optionsBuilder, new object[] {mySqlJsonOptions});
}
- public void Seed()
+ public async Task SeedAsync()
{
JsonEntities.AddRange(
new JsonEntity { Id = 1, Customer = CreateCustomer1(), ToplevelArray = new[] { "one", "two", "three" } },
new JsonEntity { Id = 2, Customer = CreateCustomer2() });
- SaveChanges();
+ await SaveChangesAsync();
}
public static Customer CreateCustomer1()
@@ -300,7 +301,7 @@ public class JsonPocoChangeTrackingFixtureBase : ServiceProviderPerContextFixtur
{
protected override string StoreName => "JsonPocoChangeTrackingTest";
protected override ITestStoreFactory TestStoreFactory => MySqlTestStoreFactory.Instance;
- protected override void Seed(JsonPocoChangeTrackingContext context) => context.Seed();
+ protected override Task SeedAsync(JsonPocoChangeTrackingContext context) => context.SeedAsync();
public virtual DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder, MySqlJsonChangeTrackingOptions? changeTrackingOptions)
=> builder;
diff --git a/test/EFCore.MySql.FunctionalTests/Query/JsonPocoQueryTestBase.cs b/test/EFCore.MySql.FunctionalTests/Query/JsonPocoQueryTestBase.cs
index aeca7a48b..ff9b59bf5 100644
--- a/test/EFCore.MySql.FunctionalTests/Query/JsonPocoQueryTestBase.cs
+++ b/test/EFCore.MySql.FunctionalTests/Query/JsonPocoQueryTestBase.cs
@@ -2,6 +2,7 @@
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text.Json.Serialization;
+using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.TestUtilities;
using Newtonsoft.Json;
@@ -474,12 +475,12 @@ public class JsonPocoQueryContext : PoolableDbContext
public JsonPocoQueryContext(DbContextOptions options) : base(options) {}
- public static void Seed(JsonPocoQueryContext context)
+ public static async Task SeedAsync(JsonPocoQueryContext context)
{
context.JsonEntities.AddRange(
new JsonEntity { Id = 1, Customer = createCustomer1(), ToplevelArray = new[] { "one", "two", "three" } },
new JsonEntity { Id = 2, Customer = createCustomer2() });
- context.SaveChanges();
+ await context.SaveChangesAsync();
static Customer createCustomer1() => new Customer
{
@@ -567,7 +568,7 @@ public class JsonPocoQueryFixtureBase : SharedStoreFixtureBase MySqlTestStoreFactory.GuidBinary16Instance;
public TestSqlLoggerFactory TestSqlLoggerFactory => (TestSqlLoggerFactory)ListLoggerFactory;
- protected override void Seed(JsonPocoQueryContext context) => JsonPocoQueryContext.Seed(context);
+ protected override Task SeedAsync(JsonPocoQueryContext context) => JsonPocoQueryContext.SeedAsync(context);
}
public class Customer
diff --git a/test/EFCore.MySql.FunctionalTests/Query/JsonStringChangeTrackingTestBase.cs b/test/EFCore.MySql.FunctionalTests/Query/JsonStringChangeTrackingTestBase.cs
index 180f2d066..58953eece 100644
--- a/test/EFCore.MySql.FunctionalTests/Query/JsonStringChangeTrackingTestBase.cs
+++ b/test/EFCore.MySql.FunctionalTests/Query/JsonStringChangeTrackingTestBase.cs
@@ -1,5 +1,6 @@
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
+using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.TestUtilities;
@@ -293,12 +294,12 @@ private void SetGlobalJsonChangeTrackingOptions(
addOrUpdateExtensionMethod.Invoke(optionsBuilder, new object[] {mySqlJsonOptions});
}
- public void Seed()
+ public async Task SeedAsync()
{
JsonEntities.AddRange(
new JsonEntity {Id = 1, Customer = Customer1},
new JsonEntity {Id = 2, Customer = Customer2});
- SaveChanges();
+ await SaveChangesAsync();
}
public class JsonPocoChangeTrackingContextOptions
@@ -320,7 +321,7 @@ public class JsonStringChangeTrackingFixtureBase : ServiceProviderPerContextFixt
{
protected override string StoreName => "JsonStringChangeTrackingTest";
protected override ITestStoreFactory TestStoreFactory => MySqlTestStoreFactory.Instance;
- protected override void Seed(JsonStringChangeTrackingContext context) => context.Seed();
+ protected override Task SeedAsync(JsonStringChangeTrackingContext context) => context.SeedAsync();
public virtual DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder, MySqlJsonChangeTrackingOptions? changeTrackingOptions)
=> builder;
diff --git a/test/EFCore.MySql.FunctionalTests/Query/JsonStringQueryTestBase.cs b/test/EFCore.MySql.FunctionalTests/Query/JsonStringQueryTestBase.cs
index 30d640db6..7f7757591 100644
--- a/test/EFCore.MySql.FunctionalTests/Query/JsonStringQueryTestBase.cs
+++ b/test/EFCore.MySql.FunctionalTests/Query/JsonStringQueryTestBase.cs
@@ -2,6 +2,7 @@
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text.Json;
+using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.TestUtilities;
using Pomelo.EntityFrameworkCore.MySql.FunctionalTests.TestUtilities;
@@ -243,7 +244,7 @@ public class JsonStringQueryContext : PoolableDbContext
public JsonStringQueryContext(DbContextOptions options) : base(options) {}
- public static void Seed(JsonStringQueryContext context)
+ public static async Task SeedAsync(JsonStringQueryContext context)
{
const string customer1 = @"
{
@@ -303,7 +304,7 @@ public static void Seed(JsonStringQueryContext context)
context.JsonEntities.AddRange(
new JsonEntity { Id = 1, CustomerJson = customer1 },
new JsonEntity { Id = 2, CustomerJson = customer2 });
- context.SaveChanges();
+ await context.SaveChangesAsync();
}
}
@@ -320,7 +321,7 @@ public class JsonStringQueryFixtureBase : SharedStoreFixtureBase "JsonStringQueryTest";
protected override ITestStoreFactory TestStoreFactory => MySqlTestStoreFactory.Instance;
public TestSqlLoggerFactory TestSqlLoggerFactory => (TestSqlLoggerFactory)ListLoggerFactory;
- protected override void Seed(JsonStringQueryContext context) => JsonStringQueryContext.Seed(context);
+ protected override Task SeedAsync(JsonStringQueryContext context) => JsonStringQueryContext.SeedAsync(context);
}
#endregion
diff --git a/test/EFCore.MySql.FunctionalTests/Query/ManyToManyFieldsLoadMySqlTest.cs b/test/EFCore.MySql.FunctionalTests/Query/ManyToManyFieldsLoadMySqlTest.cs
index 970a5467b..3baaaa582 100644
--- a/test/EFCore.MySql.FunctionalTests/Query/ManyToManyFieldsLoadMySqlTest.cs
+++ b/test/EFCore.MySql.FunctionalTests/Query/ManyToManyFieldsLoadMySqlTest.cs
@@ -14,7 +14,7 @@ public ManyToManyFieldsLoadMySqlTest(ManyToManyFieldsLoadMySqlFixture fixture)
{
}
- public class ManyToManyFieldsLoadMySqlFixture : ManyToManyFieldsLoadFixtureBase
+ public class ManyToManyFieldsLoadMySqlFixture : ManyToManyFieldsLoadFixtureBase, ITestSqlLoggerFactory
{
public TestSqlLoggerFactory TestSqlLoggerFactory
=> (TestSqlLoggerFactory)ListLoggerFactory;
diff --git a/test/EFCore.MySql.FunctionalTests/Query/ManyToManyNoTrackingQueryMySqlTest.cs b/test/EFCore.MySql.FunctionalTests/Query/ManyToManyNoTrackingQueryMySqlTest.cs
index 52b19dc62..c08f85ce2 100644
--- a/test/EFCore.MySql.FunctionalTests/Query/ManyToManyNoTrackingQueryMySqlTest.cs
+++ b/test/EFCore.MySql.FunctionalTests/Query/ManyToManyNoTrackingQueryMySqlTest.cs
@@ -12,8 +12,5 @@ public ManyToManyNoTrackingQueryMySqlTest(ManyToManyQueryMySqlFixture fixture, I
Fixture.TestSqlLoggerFactory.Clear();
//Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper);
}
-
- protected override bool CanExecuteQueryString
- => true;
}
}
diff --git a/test/EFCore.MySql.FunctionalTests/Query/ManyToManyQueryMySqlTest.cs b/test/EFCore.MySql.FunctionalTests/Query/ManyToManyQueryMySqlTest.cs
index 1b8dce72c..4f7364f4e 100644
--- a/test/EFCore.MySql.FunctionalTests/Query/ManyToManyQueryMySqlTest.cs
+++ b/test/EFCore.MySql.FunctionalTests/Query/ManyToManyQueryMySqlTest.cs
@@ -10,8 +10,5 @@ public ManyToManyQueryMySqlTest(ManyToManyQueryMySqlFixture fixture, ITestOutput
{
Fixture.TestSqlLoggerFactory.Clear();
}
-
- protected override bool CanExecuteQueryString
- => true;
}
}
diff --git a/test/EFCore.MySql.FunctionalTests/Query/MatchQueryMySqlTest.cs b/test/EFCore.MySql.FunctionalTests/Query/MatchQueryMySqlTest.cs
index 910c774f4..de8e829eb 100644
--- a/test/EFCore.MySql.FunctionalTests/Query/MatchQueryMySqlTest.cs
+++ b/test/EFCore.MySql.FunctionalTests/Query/MatchQueryMySqlTest.cs
@@ -527,7 +527,7 @@ protected MatchQueryMySqlTestBase(TFixture fixture)
protected virtual DbContext CreateContext() => Fixture.CreateContext();
- public abstract class MatchQueryMySqlFixtureBase : SharedStoreFixtureBase, IQueryFixtureBase
+ public abstract class MatchQueryMySqlFixtureBase : SharedStoreFixtureBase, IQueryFixtureBase, ITestSqlLoggerFactory
{
protected override string StoreName { get; } = "MatchQueryTest";
public TestSqlLoggerFactory TestSqlLoggerFactory => (TestSqlLoggerFactory)ListLoggerFactory;
diff --git a/test/EFCore.MySql.FunctionalTests/Query/NonSharedPrimitiveCollectionsQueryMySqlTest.cs b/test/EFCore.MySql.FunctionalTests/Query/NonSharedPrimitiveCollectionsQueryMySqlTest.cs
index a72131386..ff692781c 100644
--- a/test/EFCore.MySql.FunctionalTests/Query/NonSharedPrimitiveCollectionsQueryMySqlTest.cs
+++ b/test/EFCore.MySql.FunctionalTests/Query/NonSharedPrimitiveCollectionsQueryMySqlTest.cs
@@ -1,8 +1,11 @@
using System;
using System.Threading.Tasks;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.TestUtilities;
using Pomelo.EntityFrameworkCore.MySql.FunctionalTests.TestUtilities;
+using Pomelo.EntityFrameworkCore.MySql.Internal;
using Pomelo.EntityFrameworkCore.MySql.Tests;
using Xunit;
@@ -378,10 +381,6 @@ LIMIT 2
""");
}
- [ConditionalFact]
- public override Task Array_of_array_is_not_supported()
- => base.Array_of_array_is_not_supported();
-
[ConditionalFact]
public override Task Multidimensional_array_is_not_supported()
=> base.Multidimensional_array_is_not_supported();
@@ -474,38 +473,123 @@ LIMIT 2
public override async Task Column_collection_inside_json_owned_entity()
{
- await base.Column_collection_inside_json_owned_entity();
+ var exception = await Assert.ThrowsAsync(() => base.Column_collection_inside_json_owned_entity());
+ Assert.Equal(MySqlStrings.Ef7CoreJsonMappingNotSupported, exception.Message);
+ }
+
+ #endregion Type mapping inference
+
+ public override async Task Parameter_collection_Count_with_column_predicate_with_default_constants()
+ {
+ await base.Parameter_collection_Count_with_column_predicate_with_default_constants();
AssertSql(
- """
-SELECT TOP(2) [t].[Id], [t].[Owned]
-FROM [TestOwner] AS [t]
+$"""
+SELECT `t`.`Id`
+FROM `TestEntity` AS `t`
WHERE (
SELECT COUNT(*)
- FROM OPENJSON(JSON_VALUE([t].[Owned], '$.Strings')) AS [s]) = 2
-""",
- //
- """
-SELECT TOP(2) [t].[Id], [t].[Owned]
-FROM [TestOwner] AS [t]
-WHERE JSON_VALUE(JSON_VALUE([t].[Owned], '$.Strings'), '$[1]') = N'bar'
+ FROM (SELECT 2 AS `Value` UNION ALL VALUES {(AppConfig.ServerVersion.Supports.ValuesWithRows ? "ROW" : string.Empty)}(999)) AS `i`
+ WHERE `i`.`Value` > `t`.`Id`) = 1
""");
}
- #endregion Type mapping inference
+ public override async Task Parameter_collection_of_ints_Contains_int_with_default_constants()
+ {
+ await base.Parameter_collection_of_ints_Contains_int_with_default_constants();
- [ConditionalFact]
- public virtual void Check_all_tests_overridden()
- => TestHelpers.AssertAllMethodsOverridden(GetType());
+ AssertSql(
+"""
+SELECT `t`.`Id`
+FROM `TestEntity` AS `t`
+WHERE `t`.`Id` IN (2, 999)
+""");
+ }
- protected override ITestStoreFactory TestStoreFactory
- => MySqlTestStoreFactory.Instance;
-}
+ public override async Task Parameter_collection_Count_with_column_predicate_with_default_constants_EF_Parameter()
+ {
+ await base.Parameter_collection_Count_with_column_predicate_with_default_constants_EF_Parameter();
+
+ AssertSql();
+ }
+
+ public override async Task Parameter_collection_of_ints_Contains_int_with_default_constants_EF_Parameter()
+ {
+ await base.Parameter_collection_of_ints_Contains_int_with_default_constants_EF_Parameter();
+
+ AssertSql();
+ }
+
+ public override async Task Parameter_collection_Count_with_column_predicate_with_default_parameters()
+ {
+ await base.Parameter_collection_Count_with_column_predicate_with_default_parameters();
+
+ AssertSql();
+ }
+
+ public override async Task Parameter_collection_of_ints_Contains_int_with_default_parameters()
+ {
+ await base.Parameter_collection_of_ints_Contains_int_with_default_parameters();
+ AssertSql();
+ }
+ public override async Task Parameter_collection_Count_with_column_predicate_with_default_parameters_EF_Constant()
+ {
+ await base.Parameter_collection_Count_with_column_predicate_with_default_parameters_EF_Constant();
+ AssertSql(
+$"""
+SELECT `t`.`Id`
+FROM `TestEntity` AS `t`
+WHERE (
+ SELECT COUNT(*)
+ FROM (SELECT 2 AS `Value` UNION ALL VALUES {(AppConfig.ServerVersion.Supports.ValuesWithRows ? "ROW" : string.Empty)}(999)) AS `i`
+ WHERE `i`.`Value` > `t`.`Id`) = 1
+""");
+ }
+ public override async Task Parameter_collection_of_ints_Contains_int_with_default_parameters_EF_Constant()
+ {
+ await base.Parameter_collection_of_ints_Contains_int_with_default_parameters_EF_Constant();
+ AssertSql(
+"""
+SELECT `t`.`Id`
+FROM `TestEntity` AS `t`
+WHERE `t`.`Id` IN (2, 999)
+""");
+ }
+ public override async Task Project_collection_from_entity_type_with_owned()
+ {
+ await base.Project_collection_from_entity_type_with_owned();
+ AssertSql(
+"""
+SELECT `t`.`Ints`
+FROM `TestEntityWithOwned` AS `t`
+""");
+ }
+
+ [ConditionalFact]
+ public virtual void Check_all_tests_overridden()
+ => MySqlTestHelpers.AssertAllMethodsOverridden(GetType());
+
+ protected override DbContextOptionsBuilder SetTranslateParameterizedCollectionsToConstants(DbContextOptionsBuilder optionsBuilder)
+ {
+ new MySqlDbContextOptionsBuilder(optionsBuilder).TranslateParameterizedCollectionsToConstants();
+
+ return optionsBuilder;
+ }
+
+ protected override DbContextOptionsBuilder SetTranslateParameterizedCollectionsToParameters(DbContextOptionsBuilder optionsBuilder)
+ {
+ new MySqlDbContextOptionsBuilder(optionsBuilder).TranslateParameterizedCollectionsToParameters();
+
+ return optionsBuilder;
+ }
+ protected override ITestStoreFactory TestStoreFactory
+ => MySqlTestStoreFactory.Instance;
+}
diff --git a/test/EFCore.MySql.FunctionalTests/Query/NorthwindAggregateOperatorsQueryMySqlTest.cs b/test/EFCore.MySql.FunctionalTests/Query/NorthwindAggregateOperatorsQueryMySqlTest.cs
index 33169eb8d..b477be8db 100644
--- a/test/EFCore.MySql.FunctionalTests/Query/NorthwindAggregateOperatorsQueryMySqlTest.cs
+++ b/test/EFCore.MySql.FunctionalTests/Query/NorthwindAggregateOperatorsQueryMySqlTest.cs
@@ -21,14 +21,14 @@ public NorthwindAggregateOperatorsQueryMySqlTest(
//Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper);
}
- public override Task Average_over_max_subquery_is_client_eval(bool async)
+ public override Task Average_over_max_subquery(bool async)
=> AssertAverage(
async,
ss => ss.Set().OrderBy(c => c.CustomerID).Take(3),
selector: c => (decimal)c.Orders.Average(o => 5 + o.OrderDetails.Max(od => od.ProductID)),
asserter: (a, b) => Assert.Equal(a, b, 12)); // added flouting point precision tolerance
- public override Task Average_over_nested_subquery_is_client_eval(bool async)
+ public override Task Average_over_nested_subquery(bool async)
=> AssertAverage(
async,
ss => ss.Set().OrderBy(c => c.CustomerID).Take(3),
@@ -79,9 +79,6 @@ await Assert.ThrowsAsync(
AssertSql();
}
- protected override bool CanExecuteQueryString
- => true;
-
private void AssertSql(params string[] expected)
=> Fixture.TestSqlLoggerFactory.AssertBaseline(expected);
diff --git a/test/EFCore.MySql.FunctionalTests/Query/NorthwindEFPropertyIncludeQueryMySqlTest.cs b/test/EFCore.MySql.FunctionalTests/Query/NorthwindEFPropertyIncludeQueryMySqlTest.cs
index aa686f551..a688fda27 100644
--- a/test/EFCore.MySql.FunctionalTests/Query/NorthwindEFPropertyIncludeQueryMySqlTest.cs
+++ b/test/EFCore.MySql.FunctionalTests/Query/NorthwindEFPropertyIncludeQueryMySqlTest.cs
@@ -24,7 +24,7 @@ public NorthwindEFPropertyIncludeQueryMySqlTest(NorthwindQueryMySqlFixture TestHelpers.AssertAllMethodsOverridden(GetType());
+ => MySqlTestHelpers.AssertAllMethodsOverridden(GetType());
public override async Task Include_collection_with_last_no_orderby(bool async)
{
diff --git a/test/EFCore.MySql.FunctionalTests/Query/NorthwindFunctionsQueryMySqlTest.MySql.cs b/test/EFCore.MySql.FunctionalTests/Query/NorthwindFunctionsQueryMySqlTest.MySql.cs
index e33025dc2..d864f3a6d 100644
--- a/test/EFCore.MySql.FunctionalTests/Query/NorthwindFunctionsQueryMySqlTest.MySql.cs
+++ b/test/EFCore.MySql.FunctionalTests/Query/NorthwindFunctionsQueryMySqlTest.MySql.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore.TestModels.Northwind;
@@ -63,745 +63,6 @@ await AssertQuery(
WHERE RPAD(`c`.`CustomerID`, 8, 'c') = 'ALFKIccc'");
}
- [ConditionalTheory]
- [InlineData(StringComparison.OrdinalIgnoreCase, 1, false)]
- [InlineData(StringComparison.OrdinalIgnoreCase, 1, true)]
- [InlineData(StringComparison.CurrentCultureIgnoreCase, 1, false)]
- [InlineData(StringComparison.CurrentCultureIgnoreCase, 1, true)]
- [InlineData(StringComparison.InvariantCultureIgnoreCase, 1, false)]
- [InlineData(StringComparison.InvariantCultureIgnoreCase, 1, true)]
- [InlineData(StringComparison.Ordinal, 0, false)]
- [InlineData(StringComparison.Ordinal, 0, true)]
- [InlineData(StringComparison.CurrentCulture, 0, false)]
- [InlineData(StringComparison.CurrentCulture, 0, true)]
- [InlineData(StringComparison.InvariantCulture, 0, false)]
- [InlineData(StringComparison.InvariantCulture, 0, true)]
- public async Task StringEquals_with_comparison_parameter(StringComparison comparison, int expected, bool async)
- {
- await AssertQuery(
- async,
- ss => ss.Set().Where(c => c.CustomerID.Equals("anton", comparison)),
- assertEmpty: expected == 0);
-
- // When the comparison parameter is not a constant, we have to use a case
- // statement
- AssertSql(
- $@"@__comparison_0='{comparison:D}'
-
-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 CASE
- WHEN @__comparison_0 IN (4, 0, 2) THEN `c`.`CustomerID` = CONVERT('anton' USING utf8mb4) COLLATE utf8mb4_bin
- ELSE (LCASE(`c`.`CustomerID`) = CONVERT(LCASE('anton') USING utf8mb4) COLLATE utf8mb4_bin) AND CONVERT(LCASE('anton') USING utf8mb4) COLLATE utf8mb4_bin IS NOT NULL
-END");
- }
-
- [ConditionalTheory]
- [MemberData(nameof(IsAsyncData))]
- public async Task StringEquals_ordinal(bool async)
- {
- await AssertQuery(
- async,
- ss => ss.Set().Where(c => c.CustomerID.Equals("anton", StringComparison.Ordinal)),
- assertEmpty: true);
-
- AssertSql(@"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` = CONVERT('anton' USING utf8mb4) COLLATE utf8mb4_bin");
- }
-
- [ConditionalTheory]
- [MemberData(nameof(IsAsyncData))]
- public async Task StringEquals_invariant(bool async)
- {
- await AssertQuery(
- async,
- ss => ss.Set().Where(c => c.CustomerID.Equals("anton", StringComparison.CurrentCulture)),
- assertEmpty: true);
-
- AssertSql(@"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` = CONVERT('anton' USING utf8mb4) COLLATE utf8mb4_bin");
- }
-
- [ConditionalTheory]
- [MemberData(nameof(IsAsyncData))]
- public async Task StringEquals_current(bool async)
- {
- await AssertQuery(
- async,
- ss => ss.Set().Where(c => c.CustomerID.Equals("anton", StringComparison.InvariantCulture)),
- assertEmpty: true);
-
- AssertSql(@"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` = CONVERT('anton' USING utf8mb4) COLLATE utf8mb4_bin");
- }
-
- [ConditionalTheory]
- [MemberData(nameof(IsAsyncData))]
- public async Task StringEquals_ordinal_ignore_case(bool async)
- {
- await AssertQuery(
- async,
- ss => ss.Set().Where(c => c.CustomerID.Equals("anton", StringComparison.OrdinalIgnoreCase)));
-
- AssertSql(
- @"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 LCASE(`c`.`CustomerID`) = CONVERT(LCASE('anton') USING utf8mb4) COLLATE utf8mb4_bin");
- }
-
- [ConditionalTheory]
- [MemberData(nameof(IsAsyncData))]
- public async Task StringEquals_current_ignore_case(bool async)
- {
- await AssertQuery(
- async,
- ss => ss.Set().Where(c => c.CustomerID.Equals("anton", StringComparison.CurrentCultureIgnoreCase)));
-
- AssertSql(
- @"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 LCASE(`c`.`CustomerID`) = CONVERT(LCASE('anton') USING utf8mb4) COLLATE utf8mb4_bin");
- }
-
- [ConditionalTheory]
- [MemberData(nameof(IsAsyncData))]
- public async Task StringEquals_invariant_ignore_case(bool async)
- {
- await AssertQuery(
- async,
- ss => ss.Set().Where(c => c.CustomerID.Equals("anton", StringComparison.InvariantCultureIgnoreCase)));
-
- AssertSql(
- @"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 LCASE(`c`.`CustomerID`) = CONVERT(LCASE('anton') USING utf8mb4) COLLATE utf8mb4_bin");
- }
-
- [ConditionalTheory]
- [InlineData(StringComparison.OrdinalIgnoreCase, 1, false)]
- [InlineData(StringComparison.OrdinalIgnoreCase, 1, true)]
- [InlineData(StringComparison.CurrentCultureIgnoreCase, 1, false)]
- [InlineData(StringComparison.CurrentCultureIgnoreCase, 1, true)]
- [InlineData(StringComparison.InvariantCultureIgnoreCase, 1, false)]
- [InlineData(StringComparison.InvariantCultureIgnoreCase, 1, true)]
- [InlineData(StringComparison.Ordinal, 0, false)]
- [InlineData(StringComparison.Ordinal, 0, true)]
- [InlineData(StringComparison.CurrentCulture, 0, false)]
- [InlineData(StringComparison.CurrentCulture, 0, true)]
- [InlineData(StringComparison.InvariantCulture, 0, false)]
- [InlineData(StringComparison.InvariantCulture, 0, true)]
- public async Task StaticStringEquals_with_comparison(StringComparison comparison, int expected, bool async)
- {
- await AssertQuery(
- async,
- ss => ss.Set().Where(c => string.Equals(c.CustomerID, "anton", comparison)),
- assertEmpty: expected == 0);
-
- // When the comparison parameter is not a constant, we have to use a case
- // statement
- AssertSql(
- $@"@__comparison_0='{comparison:D}'
-
-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 CASE
- WHEN @__comparison_0 IN (4, 0, 2) THEN `c`.`CustomerID` = CONVERT('anton' USING utf8mb4) COLLATE utf8mb4_bin
- ELSE (LCASE(`c`.`CustomerID`) = CONVERT(LCASE('anton') USING utf8mb4) COLLATE utf8mb4_bin) AND CONVERT(LCASE('anton') USING utf8mb4) COLLATE utf8mb4_bin IS NOT NULL
-END");
- }
-
- [ConditionalTheory]
- [MemberData(nameof(IsAsyncData))]
- public async Task StaticStringEquals_ordinal(bool async)
- {
- await AssertQuery(
- async,
- ss => ss.Set().Where(c => string.Equals(c.CustomerID, "anton", StringComparison.Ordinal)),
- assertEmpty: true);
-
- AssertSql(@"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` = CONVERT('anton' USING utf8mb4) COLLATE utf8mb4_bin");
- }
-
- [ConditionalTheory]
- [MemberData(nameof(IsAsyncData))]
- public async Task StaticStringEquals_invariant(bool async)
- {
- await AssertQuery(
- async,
- ss => ss.Set().Where(c => string.Equals(c.CustomerID, "anton", StringComparison.CurrentCulture)),
- assertEmpty: true);
-
- AssertSql(@"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` = CONVERT('anton' USING utf8mb4) COLLATE utf8mb4_bin");
- }
-
- [ConditionalTheory]
- [MemberData(nameof(IsAsyncData))]
- public async Task StaticStringEquals_current(bool async)
- {
- await AssertQuery(
- async,
- ss => ss.Set().Where(c => string.Equals(c.CustomerID, "anton", StringComparison.InvariantCulture)),
- assertEmpty: true);
-
- AssertSql(@"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` = CONVERT('anton' USING utf8mb4) COLLATE utf8mb4_bin");
- }
-
- [ConditionalTheory]
- [MemberData(nameof(IsAsyncData))]
- public async Task StaticStringEquals_ordinal_ignore_case(bool async)
- {
- await AssertQuery(
- async,
- ss => ss.Set().Where(c => string.Equals(c.CustomerID, "anton", StringComparison.OrdinalIgnoreCase)));
-
- AssertSql(
- @"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 LCASE(`c`.`CustomerID`) = CONVERT(LCASE('anton') USING utf8mb4) COLLATE utf8mb4_bin");
- }
-
- [ConditionalTheory]
- [MemberData(nameof(IsAsyncData))]
- public async Task StaticStringEquals_current_ignore_case(bool async)
- {
- await AssertQuery(
- async,
- ss => ss.Set().Where(c => string.Equals(c.CustomerID, "anton", StringComparison.CurrentCultureIgnoreCase)));
-
- AssertSql(
- @"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 LCASE(`c`.`CustomerID`) = CONVERT(LCASE('anton') USING utf8mb4) COLLATE utf8mb4_bin");
- }
-
- [ConditionalTheory]
- [MemberData(nameof(IsAsyncData))]
- public async Task StaticStringEquals_invariant_ignore_case(bool async)
- {
- await AssertQuery(
- async,
- ss => ss.Set().Where(c => string.Equals(c.CustomerID, "anton", StringComparison.InvariantCultureIgnoreCase)));
-
- AssertSql(
- @"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 LCASE(`c`.`CustomerID`) = CONVERT(LCASE('anton') USING utf8mb4) COLLATE utf8mb4_bin");
- }
-
- [ConditionalTheory]
- [InlineData(StringComparison.OrdinalIgnoreCase, 1, false)]
- [InlineData(StringComparison.OrdinalIgnoreCase, 1, true)]
- [InlineData(StringComparison.CurrentCultureIgnoreCase, 1, false)]
- [InlineData(StringComparison.CurrentCultureIgnoreCase, 1, true)]
- [InlineData(StringComparison.InvariantCultureIgnoreCase, 1, false)]
- [InlineData(StringComparison.InvariantCultureIgnoreCase, 1, true)]
- [InlineData(StringComparison.Ordinal, 0, false)]
- [InlineData(StringComparison.Ordinal, 0, true)]
- [InlineData(StringComparison.CurrentCulture, 0, false)]
- [InlineData(StringComparison.CurrentCulture, 0, true)]
- [InlineData(StringComparison.InvariantCulture, 0, false)]
- [InlineData(StringComparison.InvariantCulture, 0, true)]
- public async Task StringContains_with_comparison(StringComparison comparison, int expected, bool async)
- {
- await AssertQuery(
- async,
- ss => ss.Set().Where(c => c.CustomerID.Contains("nto", comparison)),
- assertEmpty: expected == 0);
-
- // When the comparison parameter is not a constant, we have to use a case
- // statement
- AssertSql(
- $@"@__comparison_0='{comparison:D}'
-
-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 CASE
- WHEN @__comparison_0 IN (4, 0, 2) THEN `c`.`CustomerID` LIKE CONVERT('%nto%' USING utf8mb4) COLLATE utf8mb4_bin
- ELSE ((LCASE(`c`.`CustomerID`) LIKE CONVERT(LCASE('%nto%') USING utf8mb4) COLLATE utf8mb4_bin) AND TRUE) AND CONVERT(LCASE('%nto%') USING utf8mb4) COLLATE utf8mb4_bin IS NOT NULL
-END");
- }
-
- [ConditionalTheory]
- [MemberData(nameof(IsAsyncData))]
- public async Task StringContains_ordinal(bool async)
- {
- await AssertQuery(
- async,
- ss => ss.Set().Where(c => c.CustomerID.Contains("nto", StringComparison.Ordinal)),
- assertEmpty: true);
-
- AssertSql(
- @"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 CONVERT('%nto%' USING utf8mb4) COLLATE utf8mb4_bin");
- }
-
- [ConditionalTheory]
- [MemberData(nameof(IsAsyncData))]
- public async Task StringContains_invariant(bool async)
- {
- await AssertQuery(
- async,
- ss => ss.Set().Where(c => c.CustomerID.Contains("nto", StringComparison.CurrentCulture)),
- assertEmpty: true);
-
- AssertSql(
- @"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 CONVERT('%nto%' USING utf8mb4) COLLATE utf8mb4_bin");
- }
-
- [ConditionalTheory]
- [MemberData(nameof(IsAsyncData))]
- public async Task StringContains_current(bool async)
- {
- await AssertQuery(
- async,
- ss => ss.Set().Where(c => c.CustomerID.Contains("nto", StringComparison.InvariantCulture)),
- assertEmpty: true);
-
- AssertSql(
- @"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 CONVERT('%nto%' USING utf8mb4) COLLATE utf8mb4_bin");
- }
-
- [ConditionalTheory]
- [MemberData(nameof(IsAsyncData))]
- public async Task StringContains_ordinal_ignore_case(bool async)
- {
- await AssertQuery(
- async,
- ss => ss.Set().Where(c => c.CustomerID.Contains("nto", StringComparison.OrdinalIgnoreCase)));
-
- AssertSql(
- @"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 LCASE(`c`.`CustomerID`) LIKE CONVERT(LCASE('%nto%') USING utf8mb4) COLLATE utf8mb4_bin");
- }
-
- [ConditionalTheory]
- [MemberData(nameof(IsAsyncData))]
- public async Task StringContains_current_ignore_case(bool async)
- {
- await AssertQuery(
- async,
- ss => ss.Set().Where(c => c.CustomerID.Contains("nto", StringComparison.CurrentCultureIgnoreCase)));
-
- AssertSql(
- @"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 LCASE(`c`.`CustomerID`) LIKE CONVERT(LCASE('%nto%') USING utf8mb4) COLLATE utf8mb4_bin");
- }
-
- [ConditionalTheory]
- [MemberData(nameof(IsAsyncData))]
- public async Task StringContains_invariant_ignore_case(bool async)
- {
- await AssertQuery(
- async,
- ss => ss.Set().Where(c => c.CustomerID.Contains("nto", StringComparison.InvariantCultureIgnoreCase)));
-
- AssertSql(
- @"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 LCASE(`c`.`CustomerID`) LIKE CONVERT(LCASE('%nto%') USING utf8mb4) COLLATE utf8mb4_bin");
- }
-
- [ConditionalTheory]
- [InlineData(StringComparison.OrdinalIgnoreCase, 1, false)]
- [InlineData(StringComparison.OrdinalIgnoreCase, 1, true)]
- [InlineData(StringComparison.CurrentCultureIgnoreCase, 1, false)]
- [InlineData(StringComparison.CurrentCultureIgnoreCase, 1, true)]
- [InlineData(StringComparison.InvariantCultureIgnoreCase, 1, false)]
- [InlineData(StringComparison.InvariantCultureIgnoreCase, 1, true)]
- [InlineData(StringComparison.Ordinal, 0, false)]
- [InlineData(StringComparison.Ordinal, 0, true)]
- [InlineData(StringComparison.CurrentCulture, 0, false)]
- [InlineData(StringComparison.CurrentCulture, 0, true)]
- [InlineData(StringComparison.InvariantCulture, 0, false)]
- [InlineData(StringComparison.InvariantCulture, 0, true)]
- public async Task StringStartsWith_with_comparison(StringComparison comparison, int expected, bool async)
- {
- await AssertQuery(
- async,
- ss => ss.Set().Where(c => c.CustomerID.StartsWith("anto", comparison)),
- assertEmpty: expected == 0);
-
- // When the comparison parameter is not a constant, we have to use a case
- // statement
- AssertSql(
- $@"@__comparison_0='{comparison:D}'
-
-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 CASE
- WHEN @__comparison_0 IN (4, 0, 2) THEN `c`.`CustomerID` LIKE CONVERT('anto' USING utf8mb4) COLLATE utf8mb4_bin
- ELSE ((LCASE(`c`.`CustomerID`) LIKE CONVERT(LCASE('anto%') USING utf8mb4) COLLATE utf8mb4_bin) AND TRUE) AND CONVERT(LCASE('anto%') USING utf8mb4) COLLATE utf8mb4_bin IS NOT NULL
-END");
- }
-
- [ConditionalTheory]
- [MemberData(nameof(IsAsyncData))]
- public async Task StringStartsWith_ordinal(bool async)
- {
- await AssertQuery(
- async,
- ss => ss.Set().Where(c => c.CustomerID.StartsWith("anto", StringComparison.Ordinal)),
- assertEmpty: true);
-
- AssertSql(
- @"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 CONVERT('anto%' USING utf8mb4) COLLATE utf8mb4_bin");
- }
-
- [ConditionalTheory]
- [MemberData(nameof(IsAsyncData))]
- public async Task StringStartsWith_invariant(bool async)
- {
- await AssertQuery(
- async,
- ss => ss.Set().Where(c => c.CustomerID.StartsWith("anto", StringComparison.CurrentCulture)),
- assertEmpty: true);
-
- AssertSql(
- @"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 CONVERT('anto%' USING utf8mb4) COLLATE utf8mb4_bin");
- }
-
- [ConditionalTheory]
- [MemberData(nameof(IsAsyncData))]
- public async Task StringStartsWith_current(bool async)
- {
- await AssertQuery(
- async,
- ss => ss.Set().Where(c => c.CustomerID.StartsWith("anto", StringComparison.InvariantCulture)),
- assertEmpty: true);
-
- AssertSql(
- @"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 CONVERT('anto%' USING utf8mb4) COLLATE utf8mb4_bin");
- }
-
- [ConditionalTheory]
- [MemberData(nameof(IsAsyncData))]
- public async Task StringStartsWith_ordinal_ignore_case(bool async)
- {
- await AssertQuery(
- async,
- ss => ss.Set().Where(c => c.CustomerID.StartsWith("anto", StringComparison.OrdinalIgnoreCase)));
-
- AssertSql(
- @"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 LCASE(LCASE(`c`.`CustomerID`)) LIKE CONVERT(LCASE('anto%') USING utf8mb4) COLLATE utf8mb4_bin");
- }
-
- [ConditionalTheory]
- [MemberData(nameof(IsAsyncData))]
- public async Task StringStartsWith_current_ignore_case(bool async)
- {
- await AssertQuery(
- async,
- ss => ss.Set().Where(c => c.CustomerID.StartsWith("anto", StringComparison.CurrentCultureIgnoreCase)));
-
- AssertSql(
- @"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 LCASE(LCASE(`c`.`CustomerID`)) LIKE CONVERT(LCASE('anto%') USING utf8mb4) COLLATE utf8mb4_bin");
- }
-
- [ConditionalTheory]
- [MemberData(nameof(IsAsyncData))]
- public async Task StringStartsWith_invariant_ignore_case(bool async)
- {
- await AssertQuery(
- async,
- ss => ss.Set().Where(c => c.CustomerID.StartsWith("anto", StringComparison.InvariantCultureIgnoreCase)));
-
- AssertSql(
- @"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 LCASE(LCASE(`c`.`CustomerID`)) LIKE CONVERT(LCASE('anto%') USING utf8mb4) COLLATE utf8mb4_bin");
- }
-
- [ConditionalTheory]
- [InlineData(StringComparison.OrdinalIgnoreCase, 1, false)]
- [InlineData(StringComparison.OrdinalIgnoreCase, 1, true)]
- [InlineData(StringComparison.CurrentCultureIgnoreCase, 1, false)]
- [InlineData(StringComparison.CurrentCultureIgnoreCase, 1, true)]
- [InlineData(StringComparison.InvariantCultureIgnoreCase, 1, false)]
- [InlineData(StringComparison.InvariantCultureIgnoreCase, 1, true)]
- [InlineData(StringComparison.Ordinal, 0, false)]
- [InlineData(StringComparison.Ordinal, 0, true)]
- [InlineData(StringComparison.CurrentCulture, 0, false)]
- [InlineData(StringComparison.CurrentCulture, 0, true)]
- [InlineData(StringComparison.InvariantCulture, 0, false)]
- [InlineData(StringComparison.InvariantCulture, 0, true)]
- public async Task StringEndsWith_with_comparison(StringComparison comparison, int expected, bool async)
- {
- await AssertQuery(
- async,
- ss => ss.Set().Where(c => c.CustomerID.EndsWith("nton", comparison)),
- assertEmpty: expected == 0);
-
- AssertSql(
- $@"@__comparison_0='{comparison:D}'
-
-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 CASE
- WHEN @__comparison_0 IN (4, 0, 2) THEN `c`.`CustomerID` LIKE CONVERT('%nton' USING utf8mb4) COLLATE utf8mb4_bin
- ELSE ((LCASE(`c`.`CustomerID`) LIKE CONVERT(LCASE('%nton') USING utf8mb4) COLLATE utf8mb4_bin) AND TRUE) AND CONVERT(LCASE('%nton') USING utf8mb4) COLLATE utf8mb4_bin IS NOT NULL
-END");
- }
-
- [ConditionalTheory]
- [MemberData(nameof(IsAsyncData))]
- public async Task StringEndsWith_ordinal(bool async)
- {
- await AssertQuery(
- async,
- ss => ss.Set().Where(c => c.CustomerID.EndsWith("nton", StringComparison.Ordinal)),
- assertEmpty: true);
-
- AssertSql(
- @"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 CONVERT('%nton' USING utf8mb4) COLLATE utf8mb4_bin");
- }
-
- [ConditionalTheory]
- [MemberData(nameof(IsAsyncData))]
- public async Task StringEndsWith_invariant(bool async)
- {
- await AssertQuery(
- async,
- ss => ss.Set().Where(c => c.CustomerID.EndsWith("nton", StringComparison.CurrentCulture)),
- assertEmpty: true);
-
- AssertSql(
- @"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 CONVERT('%nton' USING utf8mb4) COLLATE utf8mb4_bin");
- }
-
- [ConditionalTheory]
- [MemberData(nameof(IsAsyncData))]
- public async Task StringEndsWith_current(bool async)
- {
- await AssertQuery(
- async,
- ss => ss.Set().Where(c => c.CustomerID.EndsWith("nton", StringComparison.InvariantCulture)),
- assertEmpty: true);
-
- AssertSql(
- @"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 CONVERT('%nton' USING utf8mb4) COLLATE utf8mb4_bin");
- }
-
- [ConditionalTheory]
- [MemberData(nameof(IsAsyncData))]
- public async Task StringEndsWith_ordinal_ignore_case(bool async)
- {
- await AssertQuery(
- async,
- ss => ss.Set().Where(c => c.CustomerID.EndsWith("nton", StringComparison.OrdinalIgnoreCase)));
-
- AssertSql(
- @"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 LCASE(`c`.`CustomerID`) LIKE CONVERT(LCASE('%nton') USING utf8mb4) COLLATE utf8mb4_bin");
- }
-
- [ConditionalTheory]
- [MemberData(nameof(IsAsyncData))]
- public async Task StringEndsWith_current_ignore_case(bool async)
- {
- await AssertQuery(
- async,
- ss => ss.Set().Where(c => c.CustomerID.EndsWith("nton", StringComparison.CurrentCultureIgnoreCase)));
-
- AssertSql(
- @"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 LCASE(`c`.`CustomerID`) LIKE CONVERT(LCASE('%nton') USING utf8mb4) COLLATE utf8mb4_bin");
- }
-
- [ConditionalTheory]
- [MemberData(nameof(IsAsyncData))]
- public async Task StringEndsWith_invariant_ignore_case(bool async)
- {
- await AssertQuery(
- async,
- ss => ss.Set().Where(c => c.CustomerID.EndsWith("nton", StringComparison.InvariantCultureIgnoreCase)));
-
- AssertSql(
- @"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 LCASE(`c`.`CustomerID`) LIKE CONVERT(LCASE('%nton') USING utf8mb4) COLLATE utf8mb4_bin");
- }
-
- [ConditionalTheory]
- [InlineData(StringComparison.OrdinalIgnoreCase, 1, false)]
- [InlineData(StringComparison.OrdinalIgnoreCase, 1, true)]
- [InlineData(StringComparison.CurrentCultureIgnoreCase, 1, false)]
- [InlineData(StringComparison.CurrentCultureIgnoreCase, 1, true)]
- [InlineData(StringComparison.InvariantCultureIgnoreCase, 1, false)]
- [InlineData(StringComparison.InvariantCultureIgnoreCase, 1, true)]
- [InlineData(StringComparison.Ordinal, 0, false)]
- [InlineData(StringComparison.Ordinal, 0, true)]
- [InlineData(StringComparison.CurrentCulture, 0, false)]
- [InlineData(StringComparison.CurrentCulture, 0, true)]
- [InlineData(StringComparison.InvariantCulture, 0, false)]
- [InlineData(StringComparison.InvariantCulture, 0, true)]
- public async Task StringIndexOf_with_comparison(StringComparison comparison, int expected, bool async)
- {
- await AssertQuery(
- async,
- ss => ss.Set().Where(c => c.CustomerID.IndexOf("nt", comparison) == 1),
- assertEmpty: expected == 0);
-
- // When the comparison parameter is not a constant, we have to use a case
- // statement
- AssertSql($"@__comparison_0='{comparison:D}'" + @"
-
-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 CASE
- WHEN @__comparison_0 IN (4, 0, 2) THEN LOCATE(CONVERT('nt' USING utf8mb4) COLLATE utf8mb4_bin, `c`.`CustomerID`) - 1
- ELSE LOCATE(CONVERT(LCASE('nt') USING utf8mb4) COLLATE utf8mb4_bin, LCASE(`c`.`CustomerID`)) - 1
-END = 1");
- }
-
- [ConditionalTheory]
- [MemberData(nameof(IsAsyncData))]
- public async Task StringIndexOf_with_constant_start_index(bool async)
- {
- await AssertQuery(
- async,
- ss => ss.Set().Where(c => c.CustomerID.IndexOf("nt", 0, StringComparison.OrdinalIgnoreCase) == 1));
-
- AssertSql(
- @"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 (LOCATE(CONVERT(LCASE('nt') USING utf8mb4) COLLATE utf8mb4_bin, LCASE(`c`.`CustomerID`), 1) - 1) = 1");
- }
-
- [ConditionalTheory]
- [InlineData(0, 1, false)]
- [InlineData(2, 0, false)]
- [InlineData(0, 1, true)]
- [InlineData(2, 0, true)]
- public async Task StringIndexOf_with_parameter_start_index(int startIndex, int expected, bool async)
- {
- await AssertQuery(
- async,
- ss => ss.Set().Where(c => c.CustomerID.IndexOf("nt", startIndex, StringComparison.OrdinalIgnoreCase) == 1),
- assertEmpty: expected == 0);
-
- AssertSql(
- @$"@__startIndex_0='{startIndex}'
-
-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 (LOCATE(CONVERT(LCASE('nt') USING utf8mb4) COLLATE utf8mb4_bin, LCASE(`c`.`CustomerID`), @__startIndex_0 + 1) - 1) = 1");
- }
-
- [ConditionalTheory]
- [MemberData(nameof(IsAsyncData))]
- public async Task StringIndexOf_ordinal(bool async)
- {
- await AssertQuery(
- async,
- ss => ss.Set().Where(c => c.CustomerID.IndexOf("nt", StringComparison.Ordinal) == 1),
- assertEmpty: true);
-
- AssertSql(@"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 (LOCATE(CONVERT('nt' USING utf8mb4) COLLATE utf8mb4_bin, `c`.`CustomerID`) - 1) = 1");
- }
-
- [ConditionalTheory]
- [MemberData(nameof(IsAsyncData))]
- public async Task StringIndexOf_invariant(bool async)
- {
- await AssertQuery(
- async,
- ss => ss.Set().Where(c => c.CustomerID.IndexOf("nt", StringComparison.InvariantCulture) == 1),
- assertEmpty: true);
-
- AssertSql(@"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 (LOCATE(CONVERT('nt' USING utf8mb4) COLLATE utf8mb4_bin, `c`.`CustomerID`) - 1) = 1");
- }
-
- [ConditionalTheory]
- [MemberData(nameof(IsAsyncData))]
- public async Task StringIndexOf_current(bool async)
- {
- await AssertQuery(
- async,
- ss => ss.Set().Where(c => c.CustomerID.IndexOf("nt", StringComparison.CurrentCulture) == 1),
- assertEmpty: true);
-
- AssertSql(@"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 (LOCATE(CONVERT('nt' USING utf8mb4) COLLATE utf8mb4_bin, `c`.`CustomerID`) - 1) = 1");
- }
-
- [ConditionalTheory]
- [MemberData(nameof(IsAsyncData))]
- public async Task StringIndexOf_ordinal_ignore_case(bool async)
- {
- await AssertQuery(
- async,
- ss => ss.Set().Where(c => c.CustomerID.IndexOf("nt", StringComparison.OrdinalIgnoreCase) == 1));
-
- AssertSql(@"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 (LOCATE(CONVERT(LCASE('nt') USING utf8mb4) COLLATE utf8mb4_bin, LCASE(`c`.`CustomerID`)) - 1) = 1");
- }
-
- [ConditionalTheory]
- [MemberData(nameof(IsAsyncData))]
- public async Task StringIndexOf_current_ignore_case(bool async)
- {
- await AssertQuery(
- async,
- ss => ss.Set().Where(c => c.CustomerID.IndexOf("nt", StringComparison.CurrentCultureIgnoreCase) == 1));
-
- AssertSql(@"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 (LOCATE(CONVERT(LCASE('nt') USING utf8mb4) COLLATE utf8mb4_bin, LCASE(`c`.`CustomerID`)) - 1) = 1");
- }
-
- [ConditionalTheory]
- [MemberData(nameof(IsAsyncData))]
- public async Task StringIndexOf_invariant_ignore_case(bool async)
- {
- await AssertQuery(
- async,
- ss => ss.Set().Where(c => c.CustomerID.IndexOf("nt", StringComparison.InvariantCultureIgnoreCase) == 1));
-
- AssertSql(@"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 (LOCATE(CONVERT(LCASE('nt') USING utf8mb4) COLLATE utf8mb4_bin, LCASE(`c`.`CustomerID`)) - 1) = 1");
- }
-
[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual async Task Where_math_log_new_base2(bool async)
@@ -813,10 +74,10 @@ await AssertQueryScalar(
AssertSql(
$"""
-SELECT LOG(7.0, {MySqlTestHelpers.CastAsDouble("`o`.`Discount`")})
-FROM `Order Details` AS `o`
-WHERE ((`o`.`OrderID` = 11077) AND (`o`.`Discount` > 0)) AND (LOG(7.0, {MySqlTestHelpers.CastAsDouble("`o`.`Discount`")}) < -1.0)
-""");
+ SELECT LOG(7.0, {MySqlTestHelpers.CastAsDouble("`o`.`Discount`")})
+ FROM `Order Details` AS `o`
+ WHERE ((`o`.`OrderID` = 11077) AND (`o`.`Discount` > 0)) AND (LOG(7.0, {MySqlTestHelpers.CastAsDouble("`o`.`Discount`")}) < -1.0)
+ """);
}
}
}
diff --git a/test/EFCore.MySql.FunctionalTests/Query/NorthwindFunctionsQueryMySqlTest.cs b/test/EFCore.MySql.FunctionalTests/Query/NorthwindFunctionsQueryMySqlTest.cs
index b9c7d15c2..767fb6783 100644
--- a/test/EFCore.MySql.FunctionalTests/Query/NorthwindFunctionsQueryMySqlTest.cs
+++ b/test/EFCore.MySql.FunctionalTests/Query/NorthwindFunctionsQueryMySqlTest.cs
@@ -20,9 +20,6 @@ public NorthwindFunctionsQueryMySqlTest(
//Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper);
}
- protected override bool CanExecuteQueryString
- => true;
-
[ConditionalTheory]
public override async Task String_StartsWith_Literal(bool async)
{
@@ -133,7 +130,7 @@ public override async Task String_Contains_Literal(bool async)
await base.String_Contains_Literal(async);
AssertSql(
- $@"SELECT `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`
+ @"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`.`ContactName` LIKE '%M%'");
}
@@ -156,11 +153,11 @@ public override async Task String_Contains_Column(bool async)
{
await base.String_Contains_Column(async);
- AssertSql(
+ AssertSql(
"""
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`.`ContactName` IS NOT NULL AND ((LOCATE(`c`.`ContactName`, `c`.`ContactName`) > 0) OR (`c`.`ContactName` LIKE ''))
+WHERE `c`.`ContactName` IS NOT NULL AND ((LOCATE(`c`.`ContactName`, `c`.`CompanyName`) > 0) OR (`c`.`ContactName` LIKE ''))
""");
}
@@ -170,7 +167,7 @@ public override async Task String_Contains_MethodCall(bool async)
await base.String_Contains_MethodCall(async);
AssertSql(
- $@"SELECT `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`
+ @"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`.`ContactName` LIKE '%M%'");
}
@@ -181,7 +178,7 @@ public override async Task IsNullOrWhiteSpace_in_predicate(bool async)
await base.IsNullOrWhiteSpace_in_predicate(async);
AssertSql(
- $@"SELECT `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`
+ @"SELECT `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`
FROM `Customers` AS `c`
WHERE `c`.`Region` IS NULL OR (TRIM(`c`.`Region`) = '')");
}
@@ -191,10 +188,12 @@ public override async Task Indexof_with_emptystring(bool async)
{
await base.Indexof_with_emptystring(async);
- AssertSql(
- @"SELECT `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`
+ AssertSql(
+"""
+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 (LOCATE('', `c`.`ContactName`) - 1) = 0");
+WHERE (LOCATE('', `c`.`Region`) - 1) = 0
+""");
}
[ConditionalTheory]
@@ -249,7 +248,7 @@ public override async Task Substring_with_two_args_with_zero_startindex(bool asy
await base.Substring_with_two_args_with_zero_startindex(async);
AssertSql(
- $@"SELECT SUBSTRING(`c`.`ContactName`, 0 + 1, 3)
+ @"SELECT SUBSTRING(`c`.`ContactName`, 0 + 1, 3)
FROM `Customers` AS `c`
WHERE `c`.`CustomerID` = 'ALFKI'");
}
@@ -260,7 +259,7 @@ public override async Task Substring_with_two_args_with_zero_length(bool async)
await base.Substring_with_two_args_with_zero_length(async);
AssertSql(
- $@"SELECT SUBSTRING(`c`.`ContactName`, 2 + 1, 0)
+ @"SELECT SUBSTRING(`c`.`ContactName`, 2 + 1, 0)
FROM `Customers` AS `c`
WHERE `c`.`CustomerID` = 'ALFKI'");
}
@@ -271,7 +270,7 @@ public override async Task Substring_with_two_args_with_constant(bool async)
await base.Substring_with_two_args_with_constant(async);
AssertSql(
- $@"SELECT SUBSTRING(`c`.`ContactName`, 1 + 1, 3)
+ @"SELECT SUBSTRING(`c`.`ContactName`, 1 + 1, 3)
FROM `Customers` AS `c`
WHERE `c`.`CustomerID` = 'ALFKI'");
}
@@ -282,7 +281,7 @@ public override async Task Substring_with_two_args_with_closure(bool async)
await base.Substring_with_two_args_with_closure(async);
AssertSql(
- $@"@__start_0='2'
+ @"@__start_0='2'
SELECT SUBSTRING(`c`.`ContactName`, @__start_0 + 1, 3)
FROM `Customers` AS `c`
@@ -295,7 +294,7 @@ public override async Task Substring_with_two_args_with_Index_of(bool async)
await base.Substring_with_two_args_with_Index_of(async);
AssertSql(
- $@"SELECT SUBSTRING(`c`.`ContactName`, (LOCATE('a', `c`.`ContactName`) - 1) + 1, 3)
+ @"SELECT SUBSTRING(`c`.`ContactName`, (LOCATE('a', `c`.`ContactName`) - 1) + 1, 3)
FROM `Customers` AS `c`
WHERE `c`.`CustomerID` = 'ALFKI'");
}
@@ -373,7 +372,7 @@ public override async Task Where_math_min(bool async)
await base.Where_math_min(async);
AssertSql(
- $@"SELECT `o`.`OrderID`, `o`.`ProductID`, `o`.`Discount`, `o`.`Quantity`, `o`.`UnitPrice`
+ @"SELECT `o`.`OrderID`, `o`.`ProductID`, `o`.`Discount`, `o`.`Quantity`, `o`.`UnitPrice`
FROM `Order Details` AS `o`
WHERE (`o`.`OrderID` = 11077) AND (LEAST(`o`.`OrderID`, `o`.`ProductID`) = `o`.`ProductID`)");
}
@@ -384,7 +383,7 @@ public override async Task Where_math_max(bool async)
await base.Where_math_max(async);
AssertSql(
- $@"SELECT `o`.`OrderID`, `o`.`ProductID`, `o`.`Discount`, `o`.`Quantity`, `o`.`UnitPrice`
+ @"SELECT `o`.`OrderID`, `o`.`ProductID`, `o`.`Discount`, `o`.`Quantity`, `o`.`UnitPrice`
FROM `Order Details` AS `o`
WHERE (`o`.`OrderID` = 11077) AND (GREATEST(`o`.`OrderID`, `o`.`ProductID`) = `o`.`OrderID`)");
}
@@ -395,7 +394,7 @@ public override async Task Where_string_to_lower(bool async)
await base.Where_string_to_lower(async);
AssertSql(
- $@"SELECT `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`
+ @"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 LOWER(`c`.`CustomerID`) = 'alfki'");
}
@@ -406,7 +405,7 @@ public override async Task Where_string_to_upper(bool async)
await base.Where_string_to_upper(async);
AssertSql(
- $@"SELECT `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`
+ @"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 UPPER(`c`.`CustomerID`) = 'ALFKI'");
}
@@ -417,7 +416,7 @@ public override async Task TrimStart_without_arguments_in_predicate(bool async)
await base.TrimStart_without_arguments_in_predicate(async);
AssertSql(
- $@"SELECT `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`
+ @"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 TRIM(LEADING FROM `c`.`ContactTitle`) = 'Owner'");
}
@@ -428,7 +427,7 @@ public override async Task TrimStart_with_char_argument_in_predicate(bool async)
await base.TrimStart_with_char_argument_in_predicate(async);
AssertSql(
- $@"SELECT `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`
+ @"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 TRIM(LEADING 'O' FROM `c`.`ContactTitle`) = 'wner'");
}
@@ -448,7 +447,7 @@ public override async Task TrimEnd_without_arguments_in_predicate(bool async)
await base.TrimEnd_without_arguments_in_predicate(async);
AssertSql(
- $@"SELECT `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`
+ @"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 TRIM(TRAILING FROM `c`.`ContactTitle`) = 'Owner'");
}
@@ -459,7 +458,7 @@ public override async Task TrimEnd_with_char_argument_in_predicate(bool async)
await base.TrimEnd_with_char_argument_in_predicate(async);
AssertSql(
- $@"SELECT `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`
+ @"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 TRIM(TRAILING 'r' FROM `c`.`ContactTitle`) = 'Owne'");
}
@@ -479,7 +478,7 @@ public override async Task Trim_without_argument_in_predicate(bool async)
await base.Trim_without_argument_in_predicate(async);
AssertSql(
- $@"SELECT `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`
+ @"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 TRIM(`c`.`ContactTitle`) = 'Owner'");
}
@@ -490,7 +489,7 @@ public override async Task Trim_with_char_argument_in_predicate(bool async)
await base.Trim_with_char_argument_in_predicate(async);
AssertSql(
- $@"SELECT `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`
+ @"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 TRIM('O' FROM `c`.`ContactTitle`) = 'wner'");
}
@@ -509,7 +508,7 @@ public override async Task String_FirstOrDefault_MethodCall(bool async)
await base.String_FirstOrDefault_MethodCall(async);
AssertSql(
- $@"SELECT `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`
+ @"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 SUBSTRING(`c`.`ContactName`, 1, 1) = 'A'");
}
@@ -519,7 +518,7 @@ public override async Task String_Contains_constant_with_whitespace(bool async)
await base.String_Contains_constant_with_whitespace(async);
AssertSql(
- $@"SELECT `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`
+ @"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`.`ContactName` LIKE '% %'");
}
@@ -543,7 +542,7 @@ public override async Task String_LastOrDefault_MethodCall(bool async)
await base.String_LastOrDefault_MethodCall(async);
AssertSql(
- $@"SELECT `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`
+ @"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 SUBSTRING(`c`.`ContactName`, CHAR_LENGTH(`c`.`ContactName`), 1) = 's'");
}
@@ -623,7 +622,7 @@ public override async Task Where_math_round2(bool async)
await base.Where_math_round2(async);
AssertSql(
- $@"SELECT `o`.`OrderID`, `o`.`ProductID`, `o`.`Discount`, `o`.`Quantity`, `o`.`UnitPrice`
+ @"SELECT `o`.`OrderID`, `o`.`ProductID`, `o`.`Discount`, `o`.`Quantity`, `o`.`UnitPrice`
FROM `Order Details` AS `o`
WHERE ROUND(`o`.`UnitPrice`, 2) > 100.0");
}
@@ -675,7 +674,7 @@ public override async Task Where_math_log_new_base(bool async)
AssertSql(
$@"SELECT `o`.`OrderID`, `o`.`ProductID`, `o`.`Discount`, `o`.`Quantity`, `o`.`UnitPrice`
FROM `Order Details` AS `o`
-WHERE ((`o`.`OrderID` = 11077) AND (`o`.`Discount` > 0)) AND (LOG(7.0, {MySqlTestHelpers.CastAsDouble("`o`.`Discount`")}) < 0.0)");
+WHERE ((`o`.`OrderID` = 11077) AND (`o`.`Discount` > 0)) AND (LOG(7.0, {MySqlTestHelpers.CastAsDouble("`o`.`Discount`")}) < -1.0)");
}
public override async Task Where_math_sqrt(bool async)
@@ -763,7 +762,7 @@ public override async Task Where_math_sign(bool async)
await base.Where_math_sign(async);
AssertSql(
- $@"SELECT `o`.`OrderID`, `o`.`ProductID`, `o`.`Discount`, `o`.`Quantity`, `o`.`UnitPrice`
+ @"SELECT `o`.`OrderID`, `o`.`ProductID`, `o`.`Discount`, `o`.`Quantity`, `o`.`UnitPrice`
FROM `Order Details` AS `o`
WHERE (`o`.`OrderID` = 11077) AND (SIGN(`o`.`Discount`) > 0)");
}
@@ -793,7 +792,7 @@ public override async Task IsNullOrEmpty_in_predicate(bool async)
await base.IsNullOrEmpty_in_predicate(async);
AssertSql(
- $@"SELECT `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`
+ @"SELECT `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`
FROM `Customers` AS `c`
WHERE `c`.`Region` IS NULL OR (`c`.`Region` = '')");
}
@@ -813,7 +812,7 @@ public override async Task IsNullOrWhiteSpace_in_predicate_on_non_nullable_colum
await base.IsNullOrWhiteSpace_in_predicate_on_non_nullable_column(async);
AssertSql(
- $@"SELECT `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`
+ @"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 TRIM(`c`.`CustomerID`) = ''");
}
@@ -823,7 +822,7 @@ public override async Task Order_by_length_twice(bool async)
await base.Order_by_length_twice(async);
AssertSql(
- $@"SELECT `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`
+ @"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 CHAR_LENGTH(`c`.`CustomerID`), `c`.`CustomerID`");
}
@@ -833,7 +832,7 @@ public override async Task Order_by_length_twice_followed_by_projection_of_naked
await base.Order_by_length_twice_followed_by_projection_of_naked_collection_navigation(async);
AssertSql(
- $@"SELECT `c`.`CustomerID`, `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ @"SELECT `c`.`CustomerID`, `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Customers` AS `c`
LEFT JOIN `Orders` AS `o` ON `c`.`CustomerID` = `o`.`CustomerID`
ORDER BY CHAR_LENGTH(`c`.`CustomerID`), `c`.`CustomerID`");
@@ -844,7 +843,7 @@ public override async Task Static_string_equals_in_predicate(bool async)
await base.Static_string_equals_in_predicate(async);
AssertSql(
- $@"SELECT `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`
+ @"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` = 'ANATR'");
}
@@ -854,7 +853,7 @@ public override async Task Static_equals_nullable_datetime_compared_to_non_nulla
await base.Static_equals_nullable_datetime_compared_to_non_nullable(async);
AssertSql(
- $@"@__arg_0='1996-07-04T00:00:00.0000000' (DbType = DateTime)
+ @"@__arg_0='1996-07-04T00:00:00.0000000' (DbType = DateTime)
SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
@@ -866,7 +865,7 @@ public override async Task Static_equals_int_compared_to_long(bool async)
await base.Static_equals_int_compared_to_long(async);
AssertSql(
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ @"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
WHERE FALSE");
}
@@ -1047,7 +1046,7 @@ public override async Task String_Compare_simple_more_than_one(bool async)
await base.String_Compare_simple_more_than_one(async);
AssertSql(
- $@"SELECT `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`
+ @"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 CASE
WHEN `c`.`CustomerID` = 'ALFKI' THEN 0
@@ -1055,7 +1054,7 @@ WHERE CASE
WHEN `c`.`CustomerID` < 'ALFKI' THEN -1
END = 42",
//
- $@"SELECT `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`
+ @"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 CASE
WHEN `c`.`CustomerID` = 'ALFKI' THEN 0
@@ -1063,7 +1062,7 @@ WHERE CASE
WHEN `c`.`CustomerID` < 'ALFKI' THEN -1
END > 42",
//
- $@"SELECT `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`
+ @"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 42 > CASE
WHEN `c`.`CustomerID` = 'ALFKI' THEN 0
@@ -1077,27 +1076,27 @@ public override async Task String_Compare_nested(bool async)
await base.String_Compare_nested(async);
AssertSql(
- $@"SELECT `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`
+ @"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` = (CONCAT('M', `c`.`CustomerID`))",
//
- $@"SELECT `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`
+ @"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` <> UPPER(`c`.`CustomerID`)",
//
- $@"SELECT `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`
+ @"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` > REPLACE('ALFKI', 'ALF', `c`.`CustomerID`)",
//
- $@"SELECT `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`
+ @"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` <= (CONCAT('M', `c`.`CustomerID`))",
//
- $@"SELECT `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`
+ @"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` > UPPER(`c`.`CustomerID`)",
//
- $@"SELECT `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`
+ @"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` < REPLACE('ALFKI', 'ALF', `c`.`CustomerID`)");
}
@@ -1107,11 +1106,11 @@ public override async Task String_Compare_multi_predicate(bool async)
await base.String_Compare_multi_predicate(async);
AssertSql(
- $@"SELECT `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`
+ @"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` >= 'ALFKI') AND (`c`.`CustomerID` < 'CACTU')",
//
- $@"SELECT `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`
+ @"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`.`ContactTitle` = 'Owner') AND ((`c`.`Country` <> 'USA') OR `c`.`Country` IS NULL)");
}
@@ -1259,7 +1258,7 @@ public override async Task String_Compare_to_simple_more_than_one(bool async)
await base.String_Compare_to_simple_more_than_one(async);
AssertSql(
- $@"SELECT `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`
+ @"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 CASE
WHEN `c`.`CustomerID` = 'ALFKI' THEN 0
@@ -1267,7 +1266,7 @@ WHERE CASE
WHEN `c`.`CustomerID` < 'ALFKI' THEN -1
END = 42",
//
- $@"SELECT `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`
+ @"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 CASE
WHEN `c`.`CustomerID` = 'ALFKI' THEN 0
@@ -1275,7 +1274,7 @@ WHERE CASE
WHEN `c`.`CustomerID` < 'ALFKI' THEN -1
END > 42",
//
- $@"SELECT `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`
+ @"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 42 > CASE
WHEN `c`.`CustomerID` = 'ALFKI' THEN 0
@@ -1331,11 +1330,11 @@ public override async Task String_Compare_to_multi_predicate(bool async)
await base.String_Compare_to_multi_predicate(async);
AssertSql(
- $@"SELECT `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`
+ @"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` >= 'ALFKI') AND (`c`.`CustomerID` < 'CACTU')",
//
- $@"SELECT `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`
+ @"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`.`ContactTitle` = 'Owner') AND ((`c`.`Country` <> 'USA') OR `c`.`Country` IS NULL)");
}
@@ -1345,37 +1344,37 @@ public override async Task DateTime_Compare_to_simple_zero(bool async, bool comp
await base.DateTime_Compare_to_simple_zero(async, compareTo);
AssertSql(
- $@"@__myDatetime_0='1998-05-04T00:00:00.0000000' (DbType = DateTime)
+ @"@__myDatetime_0='1998-05-04T00:00:00.0000000' (DbType = DateTime)
SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
WHERE `o`.`OrderDate` = @__myDatetime_0",
//
- $@"@__myDatetime_0='1998-05-04T00:00:00.0000000' (DbType = DateTime)
+ @"@__myDatetime_0='1998-05-04T00:00:00.0000000' (DbType = DateTime)
SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
WHERE (`o`.`OrderDate` <> @__myDatetime_0) OR `o`.`OrderDate` IS NULL",
//
- $@"@__myDatetime_0='1998-05-04T00:00:00.0000000' (DbType = DateTime)
+ @"@__myDatetime_0='1998-05-04T00:00:00.0000000' (DbType = DateTime)
SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
WHERE `o`.`OrderDate` > @__myDatetime_0",
//
- $@"@__myDatetime_0='1998-05-04T00:00:00.0000000' (DbType = DateTime)
+ @"@__myDatetime_0='1998-05-04T00:00:00.0000000' (DbType = DateTime)
SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
WHERE `o`.`OrderDate` <= @__myDatetime_0",
//
- $@"@__myDatetime_0='1998-05-04T00:00:00.0000000' (DbType = DateTime)
+ @"@__myDatetime_0='1998-05-04T00:00:00.0000000' (DbType = DateTime)
SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
WHERE `o`.`OrderDate` > @__myDatetime_0",
//
- $@"@__myDatetime_0='1998-05-04T00:00:00.0000000' (DbType = DateTime)
+ @"@__myDatetime_0='1998-05-04T00:00:00.0000000' (DbType = DateTime)
SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
@@ -1387,37 +1386,37 @@ public override async Task TimeSpan_Compare_to_simple_zero(bool async, bool comp
await base.TimeSpan_Compare_to_simple_zero(async, compareTo);
AssertSql(
- $@"@__myDatetime_0='1998-05-04T00:00:00.0000000' (DbType = DateTime)
+ @"@__myDatetime_0='1998-05-04T00:00:00.0000000' (DbType = DateTime)
SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
WHERE `o`.`OrderDate` = @__myDatetime_0",
//
- $@"@__myDatetime_0='1998-05-04T00:00:00.0000000' (DbType = DateTime)
+ @"@__myDatetime_0='1998-05-04T00:00:00.0000000' (DbType = DateTime)
SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
WHERE (`o`.`OrderDate` <> @__myDatetime_0) OR `o`.`OrderDate` IS NULL",
//
- $@"@__myDatetime_0='1998-05-04T00:00:00.0000000' (DbType = DateTime)
+ @"@__myDatetime_0='1998-05-04T00:00:00.0000000' (DbType = DateTime)
SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
WHERE `o`.`OrderDate` > @__myDatetime_0",
//
- $@"@__myDatetime_0='1998-05-04T00:00:00.0000000' (DbType = DateTime)
+ @"@__myDatetime_0='1998-05-04T00:00:00.0000000' (DbType = DateTime)
SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
WHERE `o`.`OrderDate` <= @__myDatetime_0",
//
- $@"@__myDatetime_0='1998-05-04T00:00:00.0000000' (DbType = DateTime)
+ @"@__myDatetime_0='1998-05-04T00:00:00.0000000' (DbType = DateTime)
SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
WHERE `o`.`OrderDate` > @__myDatetime_0",
//
- $@"@__myDatetime_0='1998-05-04T00:00:00.0000000' (DbType = DateTime)
+ @"@__myDatetime_0='1998-05-04T00:00:00.0000000' (DbType = DateTime)
SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
@@ -1429,37 +1428,37 @@ public override async Task Int_Compare_to_simple_zero(bool async)
await base.Int_Compare_to_simple_zero(async);
AssertSql(
- $@"@__orderId_0='10250'
+ @"@__orderId_0='10250'
SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
WHERE `o`.`OrderID` = @__orderId_0",
//
- $@"@__orderId_0='10250'
+ @"@__orderId_0='10250'
SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
WHERE `o`.`OrderID` <> @__orderId_0",
//
- $@"@__orderId_0='10250'
+ @"@__orderId_0='10250'
SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
WHERE `o`.`OrderID` > @__orderId_0",
//
- $@"@__orderId_0='10250'
+ @"@__orderId_0='10250'
SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
WHERE `o`.`OrderID` <= @__orderId_0",
//
- $@"@__orderId_0='10250'
+ @"@__orderId_0='10250'
SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
WHERE `o`.`OrderID` > @__orderId_0",
//
- $@"@__orderId_0='10250'
+ @"@__orderId_0='10250'
SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
@@ -1470,122 +1469,192 @@ public override async Task Convert_ToBoolean(bool async)
{
await base.Convert_ToBoolean(async);
- AssertSql(
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ AssertSql(
+"""
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`CustomerID` = 'ALFKI') AND CAST(`o`.`OrderID` % 3 AS signed)",
+WHERE (`o`.`CustomerID` = 'ALFKI') AND CAST(`o`.`OrderID` % 3 AS signed)
+""",
//
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ """
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`CustomerID` = 'ALFKI') AND CAST(CAST(`o`.`OrderID` % 3 AS unsigned) AS signed)",
+WHERE (`o`.`CustomerID` = 'ALFKI') AND CAST(CAST(`o`.`OrderID` % 3 AS unsigned) AS signed)
+""",
//
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ """
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`CustomerID` = 'ALFKI') AND CAST(CAST(`o`.`OrderID` % 3 AS decimal(65,30)) AS signed)",
+WHERE (`o`.`CustomerID` = 'ALFKI') AND CAST(CAST(`o`.`OrderID` % 3 AS decimal(65,30)) AS signed)
+""",
//
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ """
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`CustomerID` = 'ALFKI') AND CAST({MySqlTestHelpers.CastAsDouble("`o`.`OrderID` % 3")} AS signed)",
+WHERE (`o`.`CustomerID` = 'ALFKI') AND CAST(CAST(`o`.`OrderID` % 3 AS double) AS signed)
+""",
//
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ """
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`CustomerID` = 'ALFKI') AND CAST({MySqlTestHelpers.CastAsDouble("`o`.`OrderID` % 3")} AS signed)",
+WHERE (`o`.`CustomerID` = 'ALFKI') AND CAST(CAST(`o`.`OrderID` % 3 AS double) AS signed)
+""",
//
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ """
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`CustomerID` = 'ALFKI') AND CAST(`o`.`OrderID` % 3 AS signed)",
+WHERE (`o`.`CustomerID` = 'ALFKI') AND CAST(`o`.`OrderID` % 3 AS signed)
+""",
//
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ """
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`CustomerID` = 'ALFKI') AND CAST(`o`.`OrderID` % 3 AS signed)",
+WHERE (`o`.`CustomerID` = 'ALFKI') AND CAST(`o`.`OrderID` % 3 AS signed)
+""",
//
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ """
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`CustomerID` = 'ALFKI') AND CAST(`o`.`OrderID` % 3 AS signed)");
+WHERE (`o`.`CustomerID` = 'ALFKI') AND CAST(`o`.`OrderID` % 3 AS signed)
+""",
+ //
+ """
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+FROM `Orders` AS `o`
+WHERE (`o`.`CustomerID` = 'ALFKI') AND CAST(`o`.`OrderID` % 3 AS signed)
+""");
}
public override async Task Convert_ToByte(bool async)
{
await base.Convert_ToByte(async);
- AssertSql(
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ AssertSql(
+"""
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS signed) AS unsigned) >= 0)",
+WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS signed) AS unsigned) >= 0)
+""",
//
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ """
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(`o`.`OrderID` % 1 AS unsigned) >= 0)",
+WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(`o`.`OrderID` % 1 AS unsigned) >= 0)
+""",
//
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ """
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS decimal(65,30)) AS unsigned) >= 0)",
+WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS decimal(65,30)) AS unsigned) >= 0)
+""",
//
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ """
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST({MySqlTestHelpers.CastAsDouble("`o`.`OrderID` % 1")} AS unsigned) >= 0)",
+WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS double) AS unsigned) >= 0)
+""",
//
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ """
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST({MySqlTestHelpers.CastAsDouble("`o`.`OrderID` % 1")} AS unsigned) >= 0)",
+WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS double) AS unsigned) >= 0)
+""",
//
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ """
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS signed) AS unsigned) >= 0)",
+WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS signed) AS unsigned) >= 0)
+""",
//
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ """
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS signed) AS unsigned) >= 0)",
+WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS signed) AS unsigned) >= 0)
+""",
//
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ """
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS signed) AS unsigned) >= 0)",
+WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS signed) AS unsigned) >= 0)
+""",
//
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ """
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+FROM `Orders` AS `o`
+WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS char) AS unsigned) >= 0)
+""",
+ //
+ """
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS char) AS unsigned) >= 0)");
+WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS char) AS unsigned) >= 0)
+""");
}
public override async Task Convert_ToDecimal(bool async)
{
await base.Convert_ToDecimal(async);
- AssertSql(
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ AssertSql(
+"""
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS signed) AS decimal(65,30)) >= 0.0)",
+WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS signed) AS decimal(65,30)) >= 0.0)
+""",
//
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ """
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS unsigned) AS decimal(65,30)) >= 0.0)",
+WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS unsigned) AS decimal(65,30)) >= 0.0)
+""",
//
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ """
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(`o`.`OrderID` % 1 AS decimal(65,30)) >= 0.0)",
+WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(`o`.`OrderID` % 1 AS decimal(65,30)) >= 0.0)
+""",
//
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ """
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST({MySqlTestHelpers.CastAsDouble("`o`.`OrderID` % 1")} AS decimal(65,30)) >= 0.0)",
+WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS double) AS decimal(65,30)) >= 0.0)
+""",
//
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ """
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST({MySqlTestHelpers.CastAsDouble("`o`.`OrderID` % 1")} AS decimal(65,30)) >= 0.0)",
+WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS double) AS decimal(65,30)) >= 0.0)
+""",
//
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ """
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS signed) AS decimal(65,30)) >= 0.0)",
+WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS signed) AS decimal(65,30)) >= 0.0)
+""",
//
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ """
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS signed) AS decimal(65,30)) >= 0.0)",
+WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS signed) AS decimal(65,30)) >= 0.0)
+""",
//
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ """
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS signed) AS decimal(65,30)) >= 0.0)",
+WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS signed) AS decimal(65,30)) >= 0.0)
+""",
//
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ """
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS char) AS decimal(65,30)) >= 0.0)");
+WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS char) AS decimal(65,30)) >= 0.0)
+""",
+ //
+ """
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+FROM `Orders` AS `o`
+WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS char) AS decimal(65,30)) >= 0.0)
+""");
}
public override async Task Convert_ToDouble(bool async)
@@ -1627,6 +1696,10 @@ public override async Task Convert_ToDouble(bool async)
//
$@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
+WHERE (`o`.`CustomerID` = 'ALFKI') AND ({MySqlTestHelpers.CastAsDouble("CAST(`o`.`OrderID` % 1 AS char)")} >= 0.0)",
+ //
+ $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+FROM `Orders` AS `o`
WHERE (`o`.`CustomerID` = 'ALFKI') AND ({MySqlTestHelpers.CastAsDouble("CAST(`o`.`OrderID` % 1 AS char)")} >= 0.0)");
}
@@ -1634,172 +1707,270 @@ public override async Task Convert_ToInt16(bool async)
{
await base.Convert_ToInt16(async);
- AssertSql(
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ AssertSql(
+"""
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(`o`.`OrderID` % 1 AS signed) >= 0)",
+WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(`o`.`OrderID` % 1 AS signed) >= 0)
+""",
//
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ """
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS unsigned) AS signed) >= 0)",
+WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS unsigned) AS signed) >= 0)
+""",
//
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ """
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS decimal(65,30)) AS signed) >= 0)",
+WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS decimal(65,30)) AS signed) >= 0)
+""",
//
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ """
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST({MySqlTestHelpers.CastAsDouble("`o`.`OrderID` % 1")} AS signed) >= 0)",
+WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS double) AS signed) >= 0)
+""",
//
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ """
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST({MySqlTestHelpers.CastAsDouble("`o`.`OrderID` % 1")} AS signed) >= 0)",
+WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS double) AS signed) >= 0)
+""",
//
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ """
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(`o`.`OrderID` % 1 AS signed) >= 0)",
+WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(`o`.`OrderID` % 1 AS signed) >= 0)
+""",
//
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ """
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(`o`.`OrderID` % 1 AS signed) >= 0)",
+WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(`o`.`OrderID` % 1 AS signed) >= 0)
+""",
//
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ """
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(`o`.`OrderID` % 1 AS signed) >= 0)",
+WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(`o`.`OrderID` % 1 AS signed) >= 0)
+""",
//
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ """
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+FROM `Orders` AS `o`
+WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS char) AS signed) >= 0)
+""",
+ //
+ """
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS char) AS signed) >= 0)");
+WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS char) AS signed) >= 0)
+""");
}
public override async Task Convert_ToInt32(bool async)
{
await base.Convert_ToInt32(async);
- AssertSql(
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ AssertSql(
+"""
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(`o`.`OrderID` % 1 AS signed) >= 0)",
+WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(`o`.`OrderID` % 1 AS signed) >= 0)
+""",
//
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ """
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS unsigned) AS signed) >= 0)",
+WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS unsigned) AS signed) >= 0)
+""",
//
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ """
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS decimal(65,30)) AS signed) >= 0)",
+WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS decimal(65,30)) AS signed) >= 0)
+""",
//
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ """
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST({MySqlTestHelpers.CastAsDouble("`o`.`OrderID` % 1")} AS signed) >= 0)",
+WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS double) AS signed) >= 0)
+""",
//
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ """
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST({MySqlTestHelpers.CastAsDouble("`o`.`OrderID` % 1")} AS signed) >= 0)",
+WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS double) AS signed) >= 0)
+""",
//
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ """
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(`o`.`OrderID` % 1 AS signed) >= 0)",
+WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(`o`.`OrderID` % 1 AS signed) >= 0)
+""",
//
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ """
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(`o`.`OrderID` % 1 AS signed) >= 0)",
+WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(`o`.`OrderID` % 1 AS signed) >= 0)
+""",
//
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ """
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+FROM `Orders` AS `o`
+WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(`o`.`OrderID` % 1 AS signed) >= 0)
+""",
+ //
+ """
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(`o`.`OrderID` % 1 AS signed) >= 0)",
+WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS char) AS signed) >= 0)
+""",
//
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ """
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS char) AS signed) >= 0)");
+WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS char) AS signed) >= 0)
+""");
}
public override async Task Convert_ToInt64(bool async)
{
await base.Convert_ToInt64(async);
- AssertSql(
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ AssertSql(
+"""
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(`o`.`OrderID` % 1 AS signed) >= 0)",
+WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(`o`.`OrderID` % 1 AS signed) >= 0)
+""",
//
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ """
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS unsigned) AS signed) >= 0)",
+WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS unsigned) AS signed) >= 0)
+""",
//
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ """
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS decimal(65,30)) AS signed) >= 0)",
+WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS decimal(65,30)) AS signed) >= 0)
+""",
//
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ """
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST({MySqlTestHelpers.CastAsDouble("`o`.`OrderID` % 1")} AS signed) >= 0)",
+WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS double) AS signed) >= 0)
+""",
//
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ """
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST({MySqlTestHelpers.CastAsDouble("`o`.`OrderID` % 1")} AS signed) >= 0)",
+WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS double) AS signed) >= 0)
+""",
//
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ """
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(`o`.`OrderID` % 1 AS signed) >= 0)",
+WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(`o`.`OrderID` % 1 AS signed) >= 0)
+""",
//
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ """
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(`o`.`OrderID` % 1 AS signed) >= 0)",
+WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(`o`.`OrderID` % 1 AS signed) >= 0)
+""",
//
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ """
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(`o`.`OrderID` % 1 AS signed) >= 0)",
+WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(`o`.`OrderID` % 1 AS signed) >= 0)
+""",
//
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ """
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+FROM `Orders` AS `o`
+WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS char) AS signed) >= 0)
+""",
+ //
+ """
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS char) AS signed) >= 0)");
+WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS char) AS signed) >= 0)
+""");
}
public override async Task Convert_ToString(bool async)
{
await base.Convert_ToString(async);
- AssertSql(
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ AssertSql(
+"""
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS signed) AS char) <> '10')",
+WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS signed) AS char) <> '10')
+""",
//
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ """
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS unsigned) AS char) <> '10')",
+WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS unsigned) AS char) <> '10')
+""",
//
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ """
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS decimal(65,30)) AS char) <> '10')",
+WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS decimal(65,30)) AS char) <> '10')
+""",
//
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ """
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST({MySqlTestHelpers.CastAsDouble("`o`.`OrderID` % 1")} AS char) <> '10')",
+WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS double) AS char) <> '10')
+""",
//
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ """
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST({MySqlTestHelpers.CastAsDouble("`o`.`OrderID` % 1")} AS char) <> '10')",
+WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS double) AS char) <> '10')
+""",
//
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ """
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS signed) AS char) <> '10')",
+WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS signed) AS char) <> '10')
+""",
//
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ """
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS signed) AS char) <> '10')",
+WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS signed) AS char) <> '10')
+""",
//
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ """
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS signed) AS char) <> '10')",
+WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(CAST(`o`.`OrderID` % 1 AS signed) AS char) <> '10')
+""",
//
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ """
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+FROM `Orders` AS `o`
+WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(`o`.`OrderID` % 1 AS char) <> '10')
+""",
+ //
+ """
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(`o`.`OrderID` % 1 AS char) <> '10')",
+WHERE (`o`.`CustomerID` = 'ALFKI') AND (CAST(`o`.`OrderID` % 1 AS char) <> '10')
+""",
//
- $@"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ """
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`CustomerID` = 'ALFKI') AND ((CAST(`o`.`OrderDate` AS char) LIKE '%1997%') OR (CAST(`o`.`OrderDate` AS char) LIKE '%1998%'))");
+WHERE (`o`.`CustomerID` = 'ALFKI') AND ((CAST(`o`.`OrderDate` AS char) LIKE '%1997%') OR (CAST(`o`.`OrderDate` AS char) LIKE '%1998%'))
+""");
}
public override async Task String_StartsWith_Parameter(bool async)
@@ -2175,11 +2346,11 @@ public override async Task Where_mathf_log_new_base(bool async)
{
await base.Where_mathf_log_new_base(async);
- AssertSql(
+ AssertSql(
"""
SELECT `o`.`OrderID`, `o`.`ProductID`, `o`.`Discount`, `o`.`Quantity`, `o`.`UnitPrice`
FROM `Order Details` AS `o`
-WHERE ((`o`.`OrderID` = 11077) AND (`o`.`Discount` > 0)) AND (LOG(7, `o`.`Discount`) < 0)
+WHERE ((`o`.`OrderID` = 11077) AND (`o`.`Discount` > 0)) AND (LOG(7, `o`.`Discount`) < -1)
""");
}
@@ -2403,9 +2574,196 @@ public override async Task Where_DateOnly_FromDateTime(bool async)
""");
}
+ public override async Task String_StartsWith_with_StringComparison_Ordinal(bool async)
+ {
+ await base.String_StartsWith_with_StringComparison_Ordinal(async);
+
+ AssertSql();
+ }
+
+ public override async Task String_StartsWith_with_StringComparison_OrdinalIgnoreCase(bool async)
+ {
+ await base.String_StartsWith_with_StringComparison_OrdinalIgnoreCase(async);
+
+ AssertSql();
+ }
+
+ public override async Task String_EndsWith_with_StringComparison_Ordinal(bool async)
+ {
+ await base.String_EndsWith_with_StringComparison_Ordinal(async);
+
+ AssertSql();
+ }
+
+ public override async Task String_EndsWith_with_StringComparison_OrdinalIgnoreCase(bool async)
+ {
+ await base.String_EndsWith_with_StringComparison_OrdinalIgnoreCase(async);
+
+ AssertSql();
+ }
+
+ public override async Task String_Contains_with_StringComparison_Ordinal(bool async)
+ {
+ await base.String_Contains_with_StringComparison_Ordinal(async);
+
+ AssertSql();
+ }
+
+ public override async Task String_Contains_with_StringComparison_OrdinalIgnoreCase(bool async)
+ {
+ await base.String_Contains_with_StringComparison_OrdinalIgnoreCase(async);
+
+ AssertSql();
+ }
+
+ public override async Task String_StartsWith_with_StringComparison_unsupported(bool async)
+ {
+ await base.String_StartsWith_with_StringComparison_unsupported(async);
+
+ AssertSql();
+ }
+
+ public override async Task String_EndsWith_with_StringComparison_unsupported(bool async)
+ {
+ await base.String_EndsWith_with_StringComparison_unsupported(async);
+
+ AssertSql();
+ }
+
+ public override async Task String_Contains_in_projection(bool async)
+ {
+ await base.String_Contains_in_projection(async);
+
+ AssertSql(
+"""
+SELECT `c`.`CustomerID` AS `Id`, `c`.`ContactName` IS NOT NULL AND ((LOCATE(`c`.`ContactName`, `c`.`CompanyName`) > 0) OR (`c`.`ContactName` LIKE '')) AS `Value`
+FROM `Customers` AS `c`
+""");
+ }
+
+ public override async Task String_Contains_negated_in_predicate(bool async)
+ {
+ await base.String_Contains_negated_in_predicate(async);
+
+ AssertSql(
+"""
+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`.`ContactName` IS NULL OR ((LOCATE(`c`.`ContactName`, `c`.`CompanyName`) <= 0) AND `c`.`ContactName` NOT LIKE '')
+""");
+ }
+
+ public override async Task String_Contains_negated_in_projection(bool async)
+ {
+ await base.String_Contains_negated_in_projection(async);
+
+ AssertSql(
+"""
+SELECT `c`.`CustomerID` AS `Id`, `c`.`ContactName` IS NULL OR ((LOCATE(`c`.`ContactName`, `c`.`CompanyName`) <= 0) AND `c`.`ContactName` NOT LIKE '') AS `Value`
+FROM `Customers` AS `c`
+""");
+ }
+
+ public override async Task String_Contains_with_StringComparison_unsupported(bool async)
+ {
+ await base.String_Contains_with_StringComparison_unsupported(async);
+
+ AssertSql();
+ }
+
+ public override async Task String_Join_non_aggregate(bool async)
+ {
+ await base.String_Join_non_aggregate(async);
+
+ AssertSql(
+"""
+@__foo_0='foo' (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 CONCAT_WS('|', `c`.`CompanyName`, @__foo_0, '', 'bar') = 'Around the Horn|foo||bar'
+""");
+ }
+
+ public override async Task Where_math_max_nested(bool async)
+ {
+ await base.Where_math_max_nested(async);
+
+ AssertSql(
+"""
+SELECT `o`.`OrderID`, `o`.`ProductID`, `o`.`Discount`, `o`.`Quantity`, `o`.`UnitPrice`
+FROM `Order Details` AS `o`
+WHERE (`o`.`OrderID` = 11077) AND (GREATEST(`o`.`OrderID`, `o`.`ProductID`, 1) = `o`.`OrderID`)
+""");
+ }
+
+ public override async Task Where_math_max_nested_twice(bool async)
+ {
+ await base.Where_math_max_nested_twice(async);
+
+ AssertSql(
+"""
+SELECT `o`.`OrderID`, `o`.`ProductID`, `o`.`Discount`, `o`.`Quantity`, `o`.`UnitPrice`
+FROM `Order Details` AS `o`
+WHERE (`o`.`OrderID` = 11077) AND (GREATEST(1, `o`.`OrderID`, 2, `o`.`ProductID`) = `o`.`OrderID`)
+""");
+ }
+
+ public override async Task Where_math_min_nested(bool async)
+ {
+ await base.Where_math_min_nested(async);
+
+ AssertSql(
+"""
+SELECT `o`.`OrderID`, `o`.`ProductID`, `o`.`Discount`, `o`.`Quantity`, `o`.`UnitPrice`
+FROM `Order Details` AS `o`
+WHERE (`o`.`OrderID` = 11077) AND (LEAST(`o`.`OrderID`, `o`.`ProductID`, 99999) = `o`.`ProductID`)
+""");
+ }
+
+ public override async Task Where_math_min_nested_twice(bool async)
+ {
+ await base.Where_math_min_nested_twice(async);
+
+ AssertSql(
+"""
+SELECT `o`.`OrderID`, `o`.`ProductID`, `o`.`Discount`, `o`.`Quantity`, `o`.`UnitPrice`
+FROM `Order Details` AS `o`
+WHERE (`o`.`OrderID` = 11077) AND (LEAST(99999, `o`.`OrderID`, 99998, `o`.`ProductID`) = `o`.`ProductID`)
+""");
+ }
+
+ public override async Task Select_ToString_IndexOf(bool async)
+ {
+ await base.Select_ToString_IndexOf(async);
+
+ AssertSql(
+"""
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+FROM `Orders` AS `o`
+WHERE (LOCATE('123', CAST(`o`.`OrderID` AS char)) - 1) = -1
+""");
+ }
+
+ public override async Task Select_IndexOf_ToString(bool async)
+ {
+ await base.Select_IndexOf_ToString(async);
+
+ AssertSql(
+"""
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+FROM `Orders` AS `o`
+WHERE (LOCATE(CAST(`o`.`OrderID` AS char), '123') - 1) = -1
+""");
+ }
+
public override Task Datetime_subtraction_TotalDays(bool async)
=> AssertTranslationFailed(() => base.Datetime_subtraction_TotalDays(async));
+ [ConditionalFact]
+ public virtual void Check_all_tests_overridden()
+ => MySqlTestHelpers.AssertAllMethodsOverridden(GetType());
+
private void AssertSql(params string[] expected)
=> Fixture.TestSqlLoggerFactory.AssertBaseline(expected);
diff --git a/test/EFCore.MySql.FunctionalTests/Query/NorthwindGroupByQueryMySqlTest.cs b/test/EFCore.MySql.FunctionalTests/Query/NorthwindGroupByQueryMySqlTest.cs
index e53d3002c..b44d9f100 100644
--- a/test/EFCore.MySql.FunctionalTests/Query/NorthwindGroupByQueryMySqlTest.cs
+++ b/test/EFCore.MySql.FunctionalTests/Query/NorthwindGroupByQueryMySqlTest.cs
@@ -1,7 +1,9 @@
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.TestUtilities;
+using Pomelo.EntityFrameworkCore.MySql.FunctionalTests.TestUtilities;
using Pomelo.EntityFrameworkCore.MySql.Infrastructure;
+using Pomelo.EntityFrameworkCore.MySql.Tests;
using Pomelo.EntityFrameworkCore.MySql.Tests.TestUtilities.Attributes;
using Xunit;
using Xunit.Abstractions;
@@ -20,9 +22,6 @@ public NorthwindGroupByQueryMySqlTest(
//Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper);
}
- protected override bool CanExecuteQueryString
- => true;
-
public override async Task AsEnumerable_in_subquery_for_GroupBy(bool async)
{
await base.AsEnumerable_in_subquery_for_GroupBy(async);
@@ -140,26 +139,3661 @@ public override async Task GroupBy_group_Distinct_Select_Distinct_aggregate(bool
GROUP BY `o`.`CustomerID`");
}
- [SupportedServerVersionCondition(nameof(ServerVersionSupport.OuterReferenceInMultiLevelSubquery))]
- public override Task GroupBy_Count_in_projection(bool async)
+ public override async Task GroupBy_Property_Select_Average(bool async)
{
- return base.GroupBy_Count_in_projection(async);
+ await base.GroupBy_Property_Select_Average(async);
+
+ AssertSql(
+"""
+SELECT AVG(CAST(`o`.`OrderID` AS double))
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
}
- [SupportedServerVersionCondition("8.0.22-mysql", "0.0.0-mariadb")]
- public override Task GroupBy_group_Where_Select_Distinct_aggregate(bool async)
+ public override async Task GroupBy_Property_Select_Average_with_group_enumerable_projected(bool async)
{
- // See https://github.com/mysql-net/MySqlConnector/issues/898.
- return base.GroupBy_group_Where_Select_Distinct_aggregate(async);
+ await base.GroupBy_Property_Select_Average_with_group_enumerable_projected(async);
+
+ AssertSql();
}
- [SupportedServerVersionCondition("8.0.0-mysql", "0.0.0-mariadb")] // Is an issue issue in MySQL 5.7.34, but not in 8.0.25.
- public override Task GroupBy_constant_with_where_on_grouping_with_aggregate_operators(bool async)
+ public override async Task GroupBy_Property_Select_Count(bool async)
{
- // See https://github.com/mysql-net/MySqlConnector/issues/980.
- return base.GroupBy_constant_with_where_on_grouping_with_aggregate_operators(async);
+ await base.GroupBy_Property_Select_Count(async);
+
+ AssertSql(
+"""
+SELECT COUNT(*)
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_Property_Select_LongCount(bool async)
+ {
+ await base.GroupBy_Property_Select_LongCount(async);
+
+ AssertSql(
+"""
+SELECT COUNT(*)
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_Property_Select_Count_with_nulls(bool async)
+ {
+ await base.GroupBy_Property_Select_Count_with_nulls(async);
+
+ AssertSql(
+"""
+SELECT `c`.`City`, COUNT(*) AS `Faxes`
+FROM `Customers` AS `c`
+GROUP BY `c`.`City`
+""");
+ }
+
+ public override async Task GroupBy_Property_Select_LongCount_with_nulls(bool async)
+ {
+ await base.GroupBy_Property_Select_LongCount_with_nulls(async);
+
+ AssertSql(
+"""
+SELECT `c`.`City`, COUNT(*) AS `Faxes`
+FROM `Customers` AS `c`
+GROUP BY `c`.`City`
+""");
+ }
+
+ public override async Task GroupBy_Property_Select_Max(bool async)
+ {
+ await base.GroupBy_Property_Select_Max(async);
+
+ AssertSql(
+"""
+SELECT MAX(`o`.`OrderID`)
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_Property_Select_Min(bool async)
+ {
+ await base.GroupBy_Property_Select_Min(async);
+
+ AssertSql(
+"""
+SELECT MIN(`o`.`OrderID`)
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_Property_Select_Sum(bool async)
+ {
+ await base.GroupBy_Property_Select_Sum(async);
+
+ AssertSql(
+"""
+SELECT COALESCE(SUM(`o`.`OrderID`), 0)
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_Property_Select_Sum_Min_Max_Avg(bool async)
+ {
+ await base.GroupBy_Property_Select_Sum_Min_Max_Avg(async);
+
+ AssertSql(
+"""
+SELECT COALESCE(SUM(`o`.`OrderID`), 0) AS `Sum`, MIN(`o`.`OrderID`) AS `Min`, MAX(`o`.`OrderID`) AS `Max`, AVG(CAST(`o`.`OrderID` AS double)) AS `Avg`
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_Property_Select_Key_Average(bool async)
+ {
+ await base.GroupBy_Property_Select_Key_Average(async);
+
+ AssertSql(
+"""
+SELECT `o`.`CustomerID` AS `Key`, AVG(CAST(`o`.`OrderID` AS double)) AS `Average`
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_Property_Select_Key_Count(bool async)
+ {
+ await base.GroupBy_Property_Select_Key_Count(async);
+
+ AssertSql(
+"""
+SELECT `o`.`CustomerID` AS `Key`, COUNT(*) AS `Count`
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_Property_Select_Key_LongCount(bool async)
+ {
+ await base.GroupBy_Property_Select_Key_LongCount(async);
+
+ AssertSql(
+"""
+SELECT `o`.`CustomerID` AS `Key`, COUNT(*) AS `LongCount`
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_Property_Select_Key_Max(bool async)
+ {
+ await base.GroupBy_Property_Select_Key_Max(async);
+
+ AssertSql(
+"""
+SELECT `o`.`CustomerID` AS `Key`, MAX(`o`.`OrderID`) AS `Max`
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_Property_Select_Key_Min(bool async)
+ {
+ await base.GroupBy_Property_Select_Key_Min(async);
+
+ AssertSql(
+"""
+SELECT `o`.`CustomerID` AS `Key`, MIN(`o`.`OrderID`) AS `Min`
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_Property_Select_Key_Sum(bool async)
+ {
+ await base.GroupBy_Property_Select_Key_Sum(async);
+
+ AssertSql(
+"""
+SELECT `o`.`CustomerID` AS `Key`, COALESCE(SUM(`o`.`OrderID`), 0) AS `Sum`
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_Property_Select_Key_Sum_Min_Max_Avg(bool async)
+ {
+ await base.GroupBy_Property_Select_Key_Sum_Min_Max_Avg(async);
+
+ AssertSql(
+"""
+SELECT `o`.`CustomerID` AS `Key`, COALESCE(SUM(`o`.`OrderID`), 0) AS `Sum`, MIN(`o`.`OrderID`) AS `Min`, MAX(`o`.`OrderID`) AS `Max`, AVG(CAST(`o`.`OrderID` AS double)) AS `Avg`
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_Property_Select_Sum_Min_Key_Max_Avg(bool async)
+ {
+ await base.GroupBy_Property_Select_Sum_Min_Key_Max_Avg(async);
+
+ AssertSql(
+"""
+SELECT COALESCE(SUM(`o`.`OrderID`), 0) AS `Sum`, MIN(`o`.`OrderID`) AS `Min`, `o`.`CustomerID` AS `Key`, MAX(`o`.`OrderID`) AS `Max`, AVG(CAST(`o`.`OrderID` AS double)) AS `Avg`
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_Property_Select_key_multiple_times_and_aggregate(bool async)
+ {
+ await base.GroupBy_Property_Select_key_multiple_times_and_aggregate(async);
+
+ AssertSql(
+"""
+SELECT `o`.`CustomerID` AS `Key1`, COALESCE(SUM(`o`.`OrderID`), 0) AS `Sum`
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_Property_Select_Key_with_constant(bool async)
+ {
+ await base.GroupBy_Property_Select_Key_with_constant(async);
+
+ AssertSql(
+"""
+SELECT `o0`.`Name`, `o0`.`CustomerID` AS `Value`, COUNT(*) AS `Count`
+FROM (
+ SELECT `o`.`CustomerID`, 'CustomerID' AS `Name`
+ FROM `Orders` AS `o`
+) AS `o0`
+GROUP BY `o0`.`Name`, `o0`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_aggregate_projecting_conditional_expression(bool async)
+ {
+ await base.GroupBy_aggregate_projecting_conditional_expression(async);
+
+ AssertSql(
+"""
+SELECT `o`.`OrderDate` AS `Key`, CASE
+ WHEN COUNT(*) = 0 THEN 1
+ ELSE COALESCE(SUM(CASE
+ WHEN (`o`.`OrderID` % 2) = 0 THEN 1
+ ELSE 0
+ END), 0) / COUNT(*)
+END AS `SomeValue`
+FROM `Orders` AS `o`
+GROUP BY `o`.`OrderDate`
+""");
+ }
+
+ public override async Task GroupBy_aggregate_projecting_conditional_expression_based_on_group_key(bool async)
+ {
+ await base.GroupBy_aggregate_projecting_conditional_expression_based_on_group_key(async);
+
+ AssertSql(
+"""
+SELECT CASE
+ WHEN `o`.`OrderDate` IS NULL THEN 'is null'
+ ELSE 'is not null'
+END AS `Key`, COALESCE(SUM(`o`.`OrderID`), 0) AS `Sum`
+FROM `Orders` AS `o`
+GROUP BY `o`.`OrderDate`
+""");
+ }
+
+ public override async Task GroupBy_with_group_key_access_thru_navigation(bool async)
+ {
+ await base.GroupBy_with_group_key_access_thru_navigation(async);
+
+ AssertSql(
+"""
+SELECT `o0`.`CustomerID` AS `Key`, COALESCE(SUM(`o`.`OrderID`), 0) AS `Aggregate`
+FROM `Order Details` AS `o`
+INNER JOIN `Orders` AS `o0` ON `o`.`OrderID` = `o0`.`OrderID`
+GROUP BY `o0`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_with_group_key_access_thru_nested_navigation(bool async)
+ {
+ await base.GroupBy_with_group_key_access_thru_nested_navigation(async);
+
+ AssertSql(
+"""
+SELECT `c`.`Country` AS `Key`, COALESCE(SUM(`o`.`OrderID`), 0) AS `Aggregate`
+FROM `Order Details` AS `o`
+INNER JOIN `Orders` AS `o0` ON `o`.`OrderID` = `o0`.`OrderID`
+LEFT JOIN `Customers` AS `c` ON `o0`.`CustomerID` = `c`.`CustomerID`
+GROUP BY `c`.`Country`
+""");
+ }
+
+ public override async Task GroupBy_with_grouping_key_using_Like(bool async)
+ {
+ await base.GroupBy_with_grouping_key_using_Like(async);
+
+ AssertSql(
+"""
+SELECT `o0`.`Key`, COUNT(*) AS `Count`
+FROM (
+ SELECT (`o`.`CustomerID` LIKE 'A%') AND `o`.`CustomerID` IS NOT NULL AS `Key`
+ FROM `Orders` AS `o`
+) AS `o0`
+GROUP BY `o0`.`Key`
+""");
+ }
+
+ public override async Task GroupBy_with_grouping_key_DateTime_Day(bool async)
+ {
+ await base.GroupBy_with_grouping_key_DateTime_Day(async);
+
+ AssertSql(
+"""
+SELECT `o0`.`Key`, COUNT(*) AS `Count`
+FROM (
+ SELECT EXTRACT(day FROM `o`.`OrderDate`) AS `Key`
+ FROM `Orders` AS `o`
+) AS `o0`
+GROUP BY `o0`.`Key`
+""");
+ }
+
+ public override async Task GroupBy_with_cast_inside_grouping_aggregate(bool async)
+ {
+ await base.GroupBy_with_cast_inside_grouping_aggregate(async);
+
+ AssertSql(
+"""
+SELECT `o`.`CustomerID` AS `Key`, COUNT(*) AS `Count`, COALESCE(SUM(CAST(`o`.`OrderID` AS signed)), 0) AS `Sum`
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task Group_by_with_arithmetic_operation_inside_aggregate(bool async)
+ {
+ await base.Group_by_with_arithmetic_operation_inside_aggregate(async);
+
+ AssertSql(
+"""
+SELECT `o`.`CustomerID` AS `Key`, COALESCE(SUM(`o`.`OrderID` + CHAR_LENGTH(`o`.`CustomerID`)), 0) AS `Sum`
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task Group_by_with_projection_into_DTO(bool async)
+ {
+ await base.Group_by_with_projection_into_DTO(async);
+
+ AssertSql(
+"""
+SELECT CAST(`o`.`OrderID` AS signed) AS `Id`, COUNT(*) AS `Count`
+FROM `Orders` AS `o`
+GROUP BY `o`.`OrderID`
+""");
+ }
+
+ public override async Task Where_select_function_groupby_followed_by_another_select_with_aggregates(bool async)
+ {
+ await base.Where_select_function_groupby_followed_by_another_select_with_aggregates(async);
+
+ AssertSql(
+"""
+SELECT `o`.`CustomerID` AS `Key`, COALESCE(SUM(CASE
+ WHEN (2020 - EXTRACT(year FROM `o`.`OrderDate`)) <= 30 THEN `o`.`OrderID`
+ ELSE 0
+END), 0) AS `Sum1`, COALESCE(SUM(CASE
+ WHEN ((2020 - EXTRACT(year FROM `o`.`OrderDate`)) > 30) AND ((2020 - EXTRACT(year FROM `o`.`OrderDate`)) <= 60) THEN `o`.`OrderID`
+ ELSE 0
+END), 0) AS `Sum2`
+FROM `Orders` AS `o`
+WHERE `o`.`CustomerID` LIKE 'A%'
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task Group_by_column_project_constant(bool async)
+ {
+ await base.Group_by_column_project_constant(async);
+
+ AssertSql(
+"""
+SELECT 42
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+ORDER BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task Key_plus_key_in_projection(bool async)
+ {
+ await base.Key_plus_key_in_projection(async);
+
+ AssertSql(
+"""
+SELECT `o`.`OrderID` + `o`.`OrderID` AS `Value`, AVG(CAST(`o`.`OrderID` AS double)) AS `Average`
+FROM `Orders` AS `o`
+LEFT JOIN `Customers` AS `c` ON `o`.`CustomerID` = `c`.`CustomerID`
+GROUP BY `o`.`OrderID`
+""");
}
+ public override async Task GroupBy_with_aggregate_through_navigation_property(bool async)
+ {
+ await base.GroupBy_with_aggregate_through_navigation_property(async);
+
+ AssertSql(
+"""
+SELECT (
+ SELECT MAX(`c`.`Region`)
+ FROM `Orders` AS `o0`
+ LEFT JOIN `Customers` AS `c` ON `o0`.`CustomerID` = `c`.`CustomerID`
+ WHERE (`o`.`EmployeeID` = `o0`.`EmployeeID`) OR (`o`.`EmployeeID` IS NULL AND (`o0`.`EmployeeID` IS NULL))) AS `max`
+FROM `Orders` AS `o`
+GROUP BY `o`.`EmployeeID`
+""");
+ }
+
+ public override async Task GroupBy_with_aggregate_containing_complex_where(bool async)
+ {
+ await base.GroupBy_with_aggregate_containing_complex_where(async);
+
+ AssertSql(
+"""
+SELECT `o`.`EmployeeID` AS `Key`, (
+ SELECT MAX(`o0`.`OrderID`)
+ FROM `Orders` AS `o0`
+ WHERE (CAST(`o0`.`EmployeeID` AS signed) = CAST(MAX(`o`.`OrderID`) * 6 AS signed)) OR (`o0`.`EmployeeID` IS NULL AND (MAX(`o`.`OrderID`) IS NULL))) AS `Max`
+FROM `Orders` AS `o`
+GROUP BY `o`.`EmployeeID`
+""");
+ }
+
+ public override async Task GroupBy_anonymous_Select_Average(bool async)
+ {
+ await base.GroupBy_anonymous_Select_Average(async);
+
+ AssertSql(
+"""
+SELECT AVG(CAST(`o`.`OrderID` AS double))
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_anonymous_Select_Count(bool async)
+ {
+ await base.GroupBy_anonymous_Select_Count(async);
+
+ AssertSql(
+"""
+SELECT COUNT(*)
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_anonymous_Select_LongCount(bool async)
+ {
+ await base.GroupBy_anonymous_Select_LongCount(async);
+
+ AssertSql(
+"""
+SELECT COUNT(*)
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_anonymous_Select_Max(bool async)
+ {
+ await base.GroupBy_anonymous_Select_Max(async);
+
+ AssertSql(
+"""
+SELECT MAX(`o`.`OrderID`)
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_anonymous_Select_Min(bool async)
+ {
+ await base.GroupBy_anonymous_Select_Min(async);
+
+ AssertSql(
+"""
+SELECT MIN(`o`.`OrderID`)
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_anonymous_Select_Sum(bool async)
+ {
+ await base.GroupBy_anonymous_Select_Sum(async);
+
+ AssertSql(
+"""
+SELECT COALESCE(SUM(`o`.`OrderID`), 0)
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_anonymous_Select_Sum_Min_Max_Avg(bool async)
+ {
+ await base.GroupBy_anonymous_Select_Sum_Min_Max_Avg(async);
+
+ AssertSql(
+"""
+SELECT COALESCE(SUM(`o`.`OrderID`), 0) AS `Sum`, MIN(`o`.`OrderID`) AS `Min`, MAX(`o`.`OrderID`) AS `Max`, AVG(CAST(`o`.`OrderID` AS double)) AS `Avg`
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_anonymous_with_alias_Select_Key_Sum(bool async)
+ {
+ await base.GroupBy_anonymous_with_alias_Select_Key_Sum(async);
+
+ AssertSql(
+"""
+SELECT `o`.`CustomerID` AS `Key`, COALESCE(SUM(`o`.`OrderID`), 0) AS `Sum`
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_Composite_Select_Average(bool async)
+ {
+ await base.GroupBy_Composite_Select_Average(async);
+
+ AssertSql(
+"""
+SELECT AVG(CAST(`o`.`OrderID` AS double))
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`, `o`.`EmployeeID`
+""");
+ }
+
+ public override async Task GroupBy_Composite_Select_Count(bool async)
+ {
+ await base.GroupBy_Composite_Select_Count(async);
+
+ AssertSql(
+"""
+SELECT COUNT(*)
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`, `o`.`EmployeeID`
+""");
+ }
+
+ public override async Task GroupBy_Composite_Select_LongCount(bool async)
+ {
+ await base.GroupBy_Composite_Select_LongCount(async);
+
+ AssertSql(
+"""
+SELECT COUNT(*)
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`, `o`.`EmployeeID`
+""");
+ }
+
+ public override async Task GroupBy_Composite_Select_Max(bool async)
+ {
+ await base.GroupBy_Composite_Select_Max(async);
+
+ AssertSql(
+"""
+SELECT MAX(`o`.`OrderID`)
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`, `o`.`EmployeeID`
+""");
+ }
+
+ public override async Task GroupBy_Composite_Select_Min(bool async)
+ {
+ await base.GroupBy_Composite_Select_Min(async);
+
+ AssertSql(
+"""
+SELECT MIN(`o`.`OrderID`)
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`, `o`.`EmployeeID`
+""");
+ }
+
+ public override async Task GroupBy_Composite_Select_Sum(bool async)
+ {
+ await base.GroupBy_Composite_Select_Sum(async);
+
+ AssertSql(
+"""
+SELECT COALESCE(SUM(`o`.`OrderID`), 0)
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`, `o`.`EmployeeID`
+""");
+ }
+
+ public override async Task GroupBy_Composite_Select_Sum_Min_Max_Avg(bool async)
+ {
+ await base.GroupBy_Composite_Select_Sum_Min_Max_Avg(async);
+
+ AssertSql(
+"""
+SELECT COALESCE(SUM(`o`.`OrderID`), 0) AS `Sum`, MIN(`o`.`OrderID`) AS `Min`, MAX(`o`.`OrderID`) AS `Max`, AVG(CAST(`o`.`OrderID` AS double)) AS `Avg`
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`, `o`.`EmployeeID`
+""");
+ }
+
+ public override async Task GroupBy_Composite_Select_Key_Average(bool async)
+ {
+ await base.GroupBy_Composite_Select_Key_Average(async);
+
+ AssertSql(
+"""
+SELECT `o`.`CustomerID`, `o`.`EmployeeID`, AVG(CAST(`o`.`OrderID` AS double)) AS `Average`
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`, `o`.`EmployeeID`
+""");
+ }
+
+ public override async Task GroupBy_Composite_Select_Key_Count(bool async)
+ {
+ await base.GroupBy_Composite_Select_Key_Count(async);
+
+ AssertSql(
+"""
+SELECT `o`.`CustomerID`, `o`.`EmployeeID`, COUNT(*) AS `Count`
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`, `o`.`EmployeeID`
+""");
+ }
+
+ public override async Task GroupBy_Composite_Select_Key_LongCount(bool async)
+ {
+ await base.GroupBy_Composite_Select_Key_LongCount(async);
+
+ AssertSql(
+"""
+SELECT `o`.`CustomerID`, `o`.`EmployeeID`, COUNT(*) AS `LongCount`
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`, `o`.`EmployeeID`
+""");
+ }
+
+ public override async Task GroupBy_Composite_Select_Key_Max(bool async)
+ {
+ await base.GroupBy_Composite_Select_Key_Max(async);
+
+ AssertSql(
+"""
+SELECT `o`.`CustomerID`, `o`.`EmployeeID`, MAX(`o`.`OrderID`) AS `Max`
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`, `o`.`EmployeeID`
+""");
+ }
+
+ public override async Task GroupBy_Composite_Select_Key_Min(bool async)
+ {
+ await base.GroupBy_Composite_Select_Key_Min(async);
+
+ AssertSql(
+"""
+SELECT `o`.`CustomerID`, `o`.`EmployeeID`, MIN(`o`.`OrderID`) AS `Min`
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`, `o`.`EmployeeID`
+""");
+ }
+
+ public override async Task GroupBy_Composite_Select_Key_Sum(bool async)
+ {
+ await base.GroupBy_Composite_Select_Key_Sum(async);
+
+ AssertSql(
+"""
+SELECT `o`.`CustomerID`, `o`.`EmployeeID`, COALESCE(SUM(`o`.`OrderID`), 0) AS `Sum`
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`, `o`.`EmployeeID`
+""");
+ }
+
+ public override async Task GroupBy_Composite_Select_Key_Sum_Min_Max_Avg(bool async)
+ {
+ await base.GroupBy_Composite_Select_Key_Sum_Min_Max_Avg(async);
+
+ AssertSql(
+"""
+SELECT `o`.`CustomerID`, `o`.`EmployeeID`, COALESCE(SUM(`o`.`OrderID`), 0) AS `Sum`, MIN(`o`.`OrderID`) AS `Min`, MAX(`o`.`OrderID`) AS `Max`, AVG(CAST(`o`.`OrderID` AS double)) AS `Avg`
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`, `o`.`EmployeeID`
+""");
+ }
+
+ public override async Task GroupBy_Composite_Select_Sum_Min_Key_Max_Avg(bool async)
+ {
+ await base.GroupBy_Composite_Select_Sum_Min_Key_Max_Avg(async);
+
+ AssertSql(
+"""
+SELECT COALESCE(SUM(`o`.`OrderID`), 0) AS `Sum`, MIN(`o`.`OrderID`) AS `Min`, `o`.`CustomerID`, `o`.`EmployeeID`, MAX(`o`.`OrderID`) AS `Max`, AVG(CAST(`o`.`OrderID` AS double)) AS `Avg`
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`, `o`.`EmployeeID`
+""");
+ }
+
+ public override async Task GroupBy_Composite_Select_Sum_Min_Key_flattened_Max_Avg(bool async)
+ {
+ await base.GroupBy_Composite_Select_Sum_Min_Key_flattened_Max_Avg(async);
+
+ AssertSql(
+"""
+SELECT COALESCE(SUM(`o`.`OrderID`), 0) AS `Sum`, MIN(`o`.`OrderID`) AS `Min`, `o`.`CustomerID`, `o`.`EmployeeID`, MAX(`o`.`OrderID`) AS `Max`, AVG(CAST(`o`.`OrderID` AS double)) AS `Avg`
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`, `o`.`EmployeeID`
+""");
+ }
+
+ public override async Task GroupBy_Dto_as_key_Select_Sum(bool async)
+ {
+ await base.GroupBy_Dto_as_key_Select_Sum(async);
+
+ AssertSql(
+"""
+SELECT COALESCE(SUM(`o`.`OrderID`), 0) AS `Sum`, `o`.`CustomerID`, `o`.`EmployeeID`
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`, `o`.`EmployeeID`
+""");
+ }
+
+ public override async Task GroupBy_Dto_as_element_selector_Select_Sum(bool async)
+ {
+ await base.GroupBy_Dto_as_element_selector_Select_Sum(async);
+
+ AssertSql(
+"""
+SELECT COALESCE(SUM(CAST(`o`.`EmployeeID` AS signed)), 0) AS `Sum`, `o`.`CustomerID` AS `Key`
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_Composite_Select_Dto_Sum_Min_Key_flattened_Max_Avg(bool async)
+ {
+ await base.GroupBy_Composite_Select_Dto_Sum_Min_Key_flattened_Max_Avg(async);
+
+ AssertSql(
+"""
+SELECT COALESCE(SUM(`o`.`OrderID`), 0) AS `Sum`, MIN(`o`.`OrderID`) AS `Min`, `o`.`CustomerID` AS `CustomerId`, `o`.`EmployeeID` AS `EmployeeId`, MAX(`o`.`OrderID`) AS `Max`, AVG(CAST(`o`.`OrderID` AS double)) AS `Avg`
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`, `o`.`EmployeeID`
+""");
+ }
+
+ public override async Task GroupBy_Composite_Select_Sum_Min_part_Key_flattened_Max_Avg(bool async)
+ {
+ await base.GroupBy_Composite_Select_Sum_Min_part_Key_flattened_Max_Avg(async);
+
+ AssertSql(
+"""
+SELECT COALESCE(SUM(`o`.`OrderID`), 0) AS `Sum`, MIN(`o`.`OrderID`) AS `Min`, `o`.`CustomerID`, MAX(`o`.`OrderID`) AS `Max`, AVG(CAST(`o`.`OrderID` AS double)) AS `Avg`
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`, `o`.`EmployeeID`
+""");
+ }
+
+ public override async Task GroupBy_Constant_Select_Sum_Min_Key_Max_Avg(bool async)
+ {
+ await base.GroupBy_Constant_Select_Sum_Min_Key_Max_Avg(async);
+
+ AssertSql(
+"""
+SELECT COALESCE(SUM(`o0`.`OrderID`), 0) AS `Sum`, MIN(`o0`.`OrderID`) AS `Min`, `o0`.`Key`, MAX(`o0`.`OrderID`) AS `Max`, AVG(CAST(`o0`.`OrderID` AS double)) AS `Avg`
+FROM (
+ SELECT `o`.`OrderID`, 2 AS `Key`
+ FROM `Orders` AS `o`
+) AS `o0`
+GROUP BY `o0`.`Key`
+""");
+ }
+
+ public override async Task GroupBy_Constant_with_element_selector_Select_Sum(bool async)
+ {
+ await base.GroupBy_Constant_with_element_selector_Select_Sum(async);
+
+ AssertSql(
+"""
+SELECT COALESCE(SUM(`o0`.`OrderID`), 0) AS `Sum`
+FROM (
+ SELECT `o`.`OrderID`, 2 AS `Key`
+ FROM `Orders` AS `o`
+) AS `o0`
+GROUP BY `o0`.`Key`
+""");
+ }
+
+ public override async Task GroupBy_Constant_with_element_selector_Select_Sum2(bool async)
+ {
+ await base.GroupBy_Constant_with_element_selector_Select_Sum2(async);
+
+ AssertSql(
+"""
+SELECT COALESCE(SUM(`o0`.`OrderID`), 0) AS `Sum`
+FROM (
+ SELECT `o`.`OrderID`, 2 AS `Key`
+ FROM `Orders` AS `o`
+) AS `o0`
+GROUP BY `o0`.`Key`
+""");
+ }
+
+ public override async Task GroupBy_Constant_with_element_selector_Select_Sum3(bool async)
+ {
+ await base.GroupBy_Constant_with_element_selector_Select_Sum3(async);
+
+ AssertSql(
+"""
+SELECT COALESCE(SUM(`o0`.`OrderID`), 0) AS `Sum`
+FROM (
+ SELECT `o`.`OrderID`, 2 AS `Key`
+ FROM `Orders` AS `o`
+) AS `o0`
+GROUP BY `o0`.`Key`
+""");
+ }
+
+ public override async Task GroupBy_after_predicate_Constant_Select_Sum_Min_Key_Max_Avg(bool async)
+ {
+ await base.GroupBy_after_predicate_Constant_Select_Sum_Min_Key_Max_Avg(async);
+
+ AssertSql(
+"""
+SELECT COALESCE(SUM(`o0`.`OrderID`), 0) AS `Sum`, MIN(`o0`.`OrderID`) AS `Min`, `o0`.`Key` AS `Random`, MAX(`o0`.`OrderID`) AS `Max`, AVG(CAST(`o0`.`OrderID` AS double)) AS `Avg`
+FROM (
+ SELECT `o`.`OrderID`, 2 AS `Key`
+ FROM `Orders` AS `o`
+ WHERE `o`.`OrderID` > 10500
+) AS `o0`
+GROUP BY `o0`.`Key`
+""");
+ }
+
+ public override async Task GroupBy_Constant_with_element_selector_Select_Sum_Min_Key_Max_Avg(bool async)
+ {
+ await base.GroupBy_Constant_with_element_selector_Select_Sum_Min_Key_Max_Avg(async);
+
+ AssertSql(
+"""
+SELECT COALESCE(SUM(`o0`.`OrderID`), 0) AS `Sum`, `o0`.`Key`
+FROM (
+ SELECT `o`.`OrderID`, 2 AS `Key`
+ FROM `Orders` AS `o`
+) AS `o0`
+GROUP BY `o0`.`Key`
+""");
+ }
+
+ public override async Task GroupBy_param_Select_Sum_Min_Key_Max_Avg(bool async)
+ {
+ await base.GroupBy_param_Select_Sum_Min_Key_Max_Avg(async);
+
+ AssertSql(
+"""
+@__a_0='2'
+
+SELECT COALESCE(SUM(`o0`.`OrderID`), 0) AS `Sum`, MIN(`o0`.`OrderID`) AS `Min`, `o0`.`Key`, MAX(`o0`.`OrderID`) AS `Max`, AVG(CAST(`o0`.`OrderID` AS double)) AS `Avg`
+FROM (
+ SELECT `o`.`OrderID`, @__a_0 AS `Key`
+ FROM `Orders` AS `o`
+) AS `o0`
+GROUP BY `o0`.`Key`
+""");
+ }
+
+ public override async Task GroupBy_param_with_element_selector_Select_Sum(bool async)
+ {
+ await base.GroupBy_param_with_element_selector_Select_Sum(async);
+
+ AssertSql(
+"""
+@__a_0='2'
+
+SELECT COALESCE(SUM(`o0`.`OrderID`), 0) AS `Sum`
+FROM (
+ SELECT `o`.`OrderID`, @__a_0 AS `Key`
+ FROM `Orders` AS `o`
+) AS `o0`
+GROUP BY `o0`.`Key`
+""");
+ }
+
+ public override async Task GroupBy_param_with_element_selector_Select_Sum2(bool async)
+ {
+ await base.GroupBy_param_with_element_selector_Select_Sum2(async);
+
+ AssertSql(
+"""
+@__a_0='2'
+
+SELECT COALESCE(SUM(`o0`.`OrderID`), 0) AS `Sum`
+FROM (
+ SELECT `o`.`OrderID`, @__a_0 AS `Key`
+ FROM `Orders` AS `o`
+) AS `o0`
+GROUP BY `o0`.`Key`
+""");
+ }
+
+ public override async Task GroupBy_param_with_element_selector_Select_Sum3(bool async)
+ {
+ await base.GroupBy_param_with_element_selector_Select_Sum3(async);
+
+ AssertSql(
+"""
+@__a_0='2'
+
+SELECT COALESCE(SUM(`o0`.`OrderID`), 0) AS `Sum`
+FROM (
+ SELECT `o`.`OrderID`, @__a_0 AS `Key`
+ FROM `Orders` AS `o`
+) AS `o0`
+GROUP BY `o0`.`Key`
+""");
+ }
+
+ public override async Task GroupBy_param_with_element_selector_Select_Sum_Min_Key_Max_Avg(bool async)
+ {
+ await base.GroupBy_param_with_element_selector_Select_Sum_Min_Key_Max_Avg(async);
+
+ AssertSql(
+"""
+@__a_0='2'
+
+SELECT COALESCE(SUM(`o0`.`OrderID`), 0) AS `Sum`, `o0`.`Key`
+FROM (
+ SELECT `o`.`OrderID`, @__a_0 AS `Key`
+ FROM `Orders` AS `o`
+) AS `o0`
+GROUP BY `o0`.`Key`
+""");
+ }
+
+ public override async Task GroupBy_anonymous_key_type_mismatch_with_aggregate(bool async)
+ {
+ await base.GroupBy_anonymous_key_type_mismatch_with_aggregate(async);
+
+ AssertSql(
+"""
+SELECT COUNT(*) AS `I0`, `o0`.`I0` AS `I1`
+FROM (
+ SELECT EXTRACT(year FROM `o`.`OrderDate`) AS `I0`
+ FROM `Orders` AS `o`
+) AS `o0`
+GROUP BY `o0`.`I0`
+ORDER BY `o0`.`I0`
+""");
+ }
+
+ public override async Task GroupBy_based_on_renamed_property_simple(bool async)
+ {
+ await base.GroupBy_based_on_renamed_property_simple(async);
+
+ AssertSql(
+"""
+SELECT `c`.`City` AS `Renamed`, COUNT(*) AS `Count`
+FROM `Customers` AS `c`
+GROUP BY `c`.`City`
+""");
+ }
+
+ public override async Task GroupBy_based_on_renamed_property_complex(bool async)
+ {
+ await base.GroupBy_based_on_renamed_property_complex(async);
+
+ AssertSql(
+"""
+SELECT `c0`.`Renamed` AS `Key`, COUNT(*) AS `Count`
+FROM (
+ SELECT DISTINCT `c`.`City` AS `Renamed`, `c`.`CustomerID`
+ FROM `Customers` AS `c`
+) AS `c0`
+GROUP BY `c0`.`Renamed`
+""");
+ }
+
+ public override async Task Join_groupby_anonymous_orderby_anonymous_projection(bool async)
+ {
+ await base.Join_groupby_anonymous_orderby_anonymous_projection(async);
+
+ AssertSql(
+"""
+SELECT `c`.`CustomerID`, `o`.`OrderDate`
+FROM `Customers` AS `c`
+INNER JOIN `Orders` AS `o` ON `c`.`CustomerID` = `o`.`CustomerID`
+GROUP BY `c`.`CustomerID`, `o`.`OrderDate`
+ORDER BY `o`.`OrderDate`
+""");
+ }
+
+ public override async Task Odata_groupby_empty_key(bool async)
+ {
+ await base.Odata_groupby_empty_key(async);
+
+ AssertSql(
+"""
+SELECT 'TotalAmount' AS `Name`, COALESCE(SUM(CAST(`o0`.`OrderID` AS decimal(65,30))), 0.0) AS `Value`
+FROM (
+ SELECT `o`.`OrderID`, 1 AS `Key`
+ FROM `Orders` AS `o`
+) AS `o0`
+GROUP BY `o0`.`Key`
+""");
+ }
+
+ public override async Task GroupBy_Property_scalar_element_selector_Average(bool async)
+ {
+ await base.GroupBy_Property_scalar_element_selector_Average(async);
+
+ AssertSql(
+"""
+SELECT AVG(CAST(`o`.`OrderID` AS double))
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_Property_scalar_element_selector_Count(bool async)
+ {
+ await base.GroupBy_Property_scalar_element_selector_Count(async);
+
+ AssertSql(
+"""
+SELECT COUNT(*)
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_Property_scalar_element_selector_LongCount(bool async)
+ {
+ await base.GroupBy_Property_scalar_element_selector_LongCount(async);
+
+ AssertSql(
+"""
+SELECT COUNT(*)
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_Property_scalar_element_selector_Max(bool async)
+ {
+ await base.GroupBy_Property_scalar_element_selector_Max(async);
+
+ AssertSql(
+"""
+SELECT MAX(`o`.`OrderID`)
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_Property_scalar_element_selector_Min(bool async)
+ {
+ await base.GroupBy_Property_scalar_element_selector_Min(async);
+
+ AssertSql(
+"""
+SELECT MIN(`o`.`OrderID`)
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_Property_scalar_element_selector_Sum(bool async)
+ {
+ await base.GroupBy_Property_scalar_element_selector_Sum(async);
+
+ AssertSql(
+"""
+SELECT COALESCE(SUM(`o`.`OrderID`), 0)
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_Property_scalar_element_selector_Sum_Min_Max_Avg(bool async)
+ {
+ await base.GroupBy_Property_scalar_element_selector_Sum_Min_Max_Avg(async);
+
+ AssertSql(
+"""
+SELECT COALESCE(SUM(`o`.`OrderID`), 0) AS `Sum`, MIN(`o`.`OrderID`) AS `Min`, MAX(`o`.`OrderID`) AS `Max`, AVG(CAST(`o`.`OrderID` AS double)) AS `Avg`
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_Property_anonymous_element_selector_Average(bool async)
+ {
+ await base.GroupBy_Property_anonymous_element_selector_Average(async);
+
+ AssertSql(
+"""
+SELECT AVG(CAST(`o`.`OrderID` AS double))
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_Property_anonymous_element_selector_Count(bool async)
+ {
+ await base.GroupBy_Property_anonymous_element_selector_Count(async);
+
+ AssertSql(
+"""
+SELECT COUNT(*)
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_Property_anonymous_element_selector_LongCount(bool async)
+ {
+ await base.GroupBy_Property_anonymous_element_selector_LongCount(async);
+
+ AssertSql(
+"""
+SELECT COUNT(*)
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_Property_anonymous_element_selector_Max(bool async)
+ {
+ await base.GroupBy_Property_anonymous_element_selector_Max(async);
+
+ AssertSql(
+"""
+SELECT MAX(`o`.`OrderID`)
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_Property_anonymous_element_selector_Min(bool async)
+ {
+ await base.GroupBy_Property_anonymous_element_selector_Min(async);
+
+ AssertSql(
+"""
+SELECT MIN(`o`.`OrderID`)
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_Property_anonymous_element_selector_Sum(bool async)
+ {
+ await base.GroupBy_Property_anonymous_element_selector_Sum(async);
+
+ AssertSql(
+"""
+SELECT COALESCE(SUM(`o`.`OrderID`), 0)
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_Property_anonymous_element_selector_Sum_Min_Max_Avg(bool async)
+ {
+ await base.GroupBy_Property_anonymous_element_selector_Sum_Min_Max_Avg(async);
+
+ AssertSql(
+"""
+SELECT COALESCE(SUM(`o`.`OrderID`), 0) AS `Sum`, MIN(`o`.`EmployeeID`) AS `Min`, MAX(`o`.`EmployeeID`) AS `Max`, AVG(CAST(`o`.`OrderID` AS double)) AS `Avg`
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_element_selector_complex_aggregate(bool async)
+ {
+ await base.GroupBy_element_selector_complex_aggregate(async);
+
+ AssertSql(
+"""
+SELECT COALESCE(SUM(`o`.`OrderID` + 1), 0)
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_element_selector_complex_aggregate2(bool async)
+ {
+ await base.GroupBy_element_selector_complex_aggregate2(async);
+
+ AssertSql(
+"""
+SELECT COALESCE(SUM(`o`.`OrderID` + 1), 0)
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_element_selector_complex_aggregate3(bool async)
+ {
+ await base.GroupBy_element_selector_complex_aggregate3(async);
+
+ AssertSql(
+"""
+SELECT COALESCE(SUM(`o`.`OrderID` + 1), 0)
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_element_selector_complex_aggregate4(bool async)
+ {
+ await base.GroupBy_element_selector_complex_aggregate4(async);
+
+ AssertSql(
+"""
+SELECT COALESCE(SUM(`o`.`OrderID` + 1), 0)
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task Element_selector_with_case_block_repeated_inside_another_case_block_in_projection(bool async)
+ {
+ await base.Element_selector_with_case_block_repeated_inside_another_case_block_in_projection(async);
+
+ AssertSql(
+"""
+SELECT `o`.`OrderID`, COALESCE(SUM(CASE
+ WHEN `o`.`CustomerID` = 'ALFKI' THEN CASE
+ WHEN `o`.`OrderID` > 1000 THEN `o`.`OrderID`
+ ELSE -`o`.`OrderID`
+ END
+ ELSE -CASE
+ WHEN `o`.`OrderID` > 1000 THEN `o`.`OrderID`
+ ELSE -`o`.`OrderID`
+ END
+END), 0) AS `Aggregate`
+FROM `Orders` AS `o`
+GROUP BY `o`.`OrderID`
+""");
+ }
+
+ public override async Task GroupBy_conditional_properties(bool async)
+ {
+ await base.GroupBy_conditional_properties(async);
+
+ AssertSql(
+"""
+SELECT `o0`.`OrderMonth`, `o0`.`CustomerID` AS `Customer`, COUNT(*) AS `Count`
+FROM (
+ SELECT `o`.`CustomerID`, NULL AS `OrderMonth`
+ FROM `Orders` AS `o`
+) AS `o0`
+GROUP BY `o0`.`OrderMonth`, `o0`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_empty_key_Aggregate(bool async)
+ {
+ await base.GroupBy_empty_key_Aggregate(async);
+
+ AssertSql(
+"""
+SELECT COALESCE(SUM(`o0`.`OrderID`), 0)
+FROM (
+ SELECT `o`.`OrderID`, 1 AS `Key`
+ FROM `Orders` AS `o`
+) AS `o0`
+GROUP BY `o0`.`Key`
+""");
+ }
+
+ public override async Task GroupBy_empty_key_Aggregate_Key(bool async)
+ {
+ await base.GroupBy_empty_key_Aggregate_Key(async);
+
+ AssertSql(
+"""
+SELECT COALESCE(SUM(`o0`.`OrderID`), 0) AS `Sum`
+FROM (
+ SELECT `o`.`OrderID`, 1 AS `Key`
+ FROM `Orders` AS `o`
+) AS `o0`
+GROUP BY `o0`.`Key`
+""");
+ }
+
+ public override async Task OrderBy_GroupBy_Aggregate(bool async)
+ {
+ await base.OrderBy_GroupBy_Aggregate(async);
+
+ AssertSql(
+"""
+SELECT COALESCE(SUM(`o`.`OrderID`), 0)
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task OrderBy_Skip_GroupBy_Aggregate(bool async)
+ {
+ await base.OrderBy_Skip_GroupBy_Aggregate(async);
+
+ AssertSql(
+"""
+@__p_0='80'
+
+SELECT AVG(CAST(`o0`.`OrderID` AS double))
+FROM (
+ SELECT `o`.`OrderID`, `o`.`CustomerID`
+ FROM `Orders` AS `o`
+ ORDER BY `o`.`OrderID`
+ LIMIT 18446744073709551610 OFFSET @__p_0
+) AS `o0`
+GROUP BY `o0`.`CustomerID`
+""");
+ }
+
+ public override async Task OrderBy_Take_GroupBy_Aggregate(bool async)
+ {
+ await base.OrderBy_Take_GroupBy_Aggregate(async);
+
+ AssertSql(
+"""
+@__p_0='500'
+
+SELECT MIN(`o0`.`OrderID`)
+FROM (
+ SELECT `o`.`OrderID`, `o`.`CustomerID`
+ FROM `Orders` AS `o`
+ ORDER BY `o`.`OrderID`
+ LIMIT @__p_0
+) AS `o0`
+GROUP BY `o0`.`CustomerID`
+""");
+ }
+
+ public override async Task OrderBy_Skip_Take_GroupBy_Aggregate(bool async)
+ {
+ await base.OrderBy_Skip_Take_GroupBy_Aggregate(async);
+
+ AssertSql(
+"""
+@__p_1='500'
+@__p_0='80'
+
+SELECT MAX(`o0`.`OrderID`)
+FROM (
+ SELECT `o`.`OrderID`, `o`.`CustomerID`
+ FROM `Orders` AS `o`
+ ORDER BY `o`.`OrderID`
+ LIMIT @__p_1 OFFSET @__p_0
+) AS `o0`
+GROUP BY `o0`.`CustomerID`
+""");
+ }
+
+ public override async Task Distinct_GroupBy_Aggregate(bool async)
+ {
+ await base.Distinct_GroupBy_Aggregate(async);
+
+ AssertSql(
+"""
+SELECT `o0`.`CustomerID` AS `Key`, COUNT(*) AS `c`
+FROM (
+ SELECT DISTINCT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ FROM `Orders` AS `o`
+) AS `o0`
+GROUP BY `o0`.`CustomerID`
+""");
+ }
+
+ public override async Task Anonymous_projection_Distinct_GroupBy_Aggregate(bool async)
+ {
+ await base.Anonymous_projection_Distinct_GroupBy_Aggregate(async);
+
+ AssertSql(
+"""
+SELECT `o0`.`EmployeeID` AS `Key`, COUNT(*) AS `c`
+FROM (
+ SELECT DISTINCT `o`.`OrderID`, `o`.`EmployeeID`
+ FROM `Orders` AS `o`
+) AS `o0`
+GROUP BY `o0`.`EmployeeID`
+""");
+ }
+
+ public override async Task SelectMany_GroupBy_Aggregate(bool async)
+ {
+ await base.SelectMany_GroupBy_Aggregate(async);
+
+ AssertSql(
+"""
+SELECT `o`.`EmployeeID` AS `Key`, COUNT(*) AS `c`
+FROM `Customers` AS `c`
+INNER JOIN `Orders` AS `o` ON `c`.`CustomerID` = `o`.`CustomerID`
+GROUP BY `o`.`EmployeeID`
+""");
+ }
+
+ public override async Task Join_GroupBy_Aggregate(bool async)
+ {
+ await base.Join_GroupBy_Aggregate(async);
+
+ AssertSql(
+"""
+SELECT `c`.`CustomerID` AS `Key`, AVG(CAST(`o`.`OrderID` AS double)) AS `Count`
+FROM `Orders` AS `o`
+INNER JOIN `Customers` AS `c` ON `o`.`CustomerID` = `c`.`CustomerID`
+GROUP BY `c`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_required_navigation_member_Aggregate(bool async)
+ {
+ await base.GroupBy_required_navigation_member_Aggregate(async);
+
+ AssertSql(
+"""
+SELECT `o0`.`CustomerID` AS `CustomerId`, COUNT(*) AS `Count`
+FROM `Order Details` AS `o`
+INNER JOIN `Orders` AS `o0` ON `o`.`OrderID` = `o0`.`OrderID`
+GROUP BY `o0`.`CustomerID`
+""");
+ }
+
+ public override async Task Join_complex_GroupBy_Aggregate(bool async)
+ {
+ await base.Join_complex_GroupBy_Aggregate(async);
+
+ AssertSql(
+"""
+@__p_0='100'
+@__p_2='50'
+@__p_1='10'
+
+SELECT `c0`.`CustomerID` AS `Key`, AVG(CAST(`o0`.`OrderID` AS double)) AS `Count`
+FROM (
+ SELECT `o`.`OrderID`, `o`.`CustomerID`
+ FROM `Orders` AS `o`
+ WHERE `o`.`OrderID` < 10400
+ ORDER BY `o`.`OrderDate`
+ LIMIT @__p_0
+) AS `o0`
+INNER JOIN (
+ SELECT `c`.`CustomerID`
+ FROM `Customers` AS `c`
+ WHERE `c`.`CustomerID` NOT IN ('DRACD', 'FOLKO')
+ ORDER BY `c`.`City`
+ LIMIT @__p_2 OFFSET @__p_1
+) AS `c0` ON `o0`.`CustomerID` = `c0`.`CustomerID`
+GROUP BY `c0`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupJoin_GroupBy_Aggregate(bool async)
+ {
+ await base.GroupJoin_GroupBy_Aggregate(async);
+
+ AssertSql(
+"""
+SELECT `o`.`CustomerID` AS `Key`, AVG(CAST(`o`.`OrderID` AS double)) AS `Average`
+FROM `Customers` AS `c`
+LEFT JOIN `Orders` AS `o` ON `c`.`CustomerID` = `o`.`CustomerID`
+WHERE `o`.`OrderID` IS NOT NULL
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupJoin_GroupBy_Aggregate_2(bool async)
+ {
+ await base.GroupJoin_GroupBy_Aggregate_2(async);
+
+ AssertSql(
+"""
+SELECT `c`.`CustomerID` AS `Key`, MAX(`c`.`City`) AS `Max`
+FROM `Customers` AS `c`
+LEFT JOIN `Orders` AS `o` ON `c`.`CustomerID` = `o`.`CustomerID`
+GROUP BY `c`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupJoin_GroupBy_Aggregate_3(bool async)
+ {
+ await base.GroupJoin_GroupBy_Aggregate_3(async);
+
+ AssertSql(
+"""
+SELECT `o`.`CustomerID` AS `Key`, AVG(CAST(`o`.`OrderID` AS double)) AS `Average`
+FROM `Orders` AS `o`
+LEFT JOIN `Customers` AS `c` ON `o`.`CustomerID` = `c`.`CustomerID`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupJoin_GroupBy_Aggregate_4(bool async)
+ {
+ await base.GroupJoin_GroupBy_Aggregate_4(async);
+
+ AssertSql(
+"""
+SELECT `c`.`CustomerID` AS `Value`, MAX(`c`.`City`) AS `Max`
+FROM `Customers` AS `c`
+LEFT JOIN `Orders` AS `o` ON `c`.`CustomerID` = `o`.`CustomerID`
+GROUP BY `c`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupJoin_GroupBy_Aggregate_5(bool async)
+ {
+ await base.GroupJoin_GroupBy_Aggregate_5(async);
+
+ AssertSql(
+"""
+SELECT `o`.`OrderID` AS `Value`, AVG(CAST(`o`.`OrderID` AS double)) AS `Average`
+FROM `Orders` AS `o`
+LEFT JOIN `Customers` AS `c` ON `o`.`CustomerID` = `c`.`CustomerID`
+GROUP BY `o`.`OrderID`
+""");
+ }
+
+ public override async Task GroupBy_optional_navigation_member_Aggregate(bool async)
+ {
+ await base.GroupBy_optional_navigation_member_Aggregate(async);
+
+ AssertSql(
+"""
+SELECT `c`.`Country`, COUNT(*) AS `Count`
+FROM `Orders` AS `o`
+LEFT JOIN `Customers` AS `c` ON `o`.`CustomerID` = `c`.`CustomerID`
+GROUP BY `c`.`Country`
+""");
+ }
+
+ public override async Task GroupJoin_complex_GroupBy_Aggregate(bool async)
+ {
+ await base.GroupJoin_complex_GroupBy_Aggregate(async);
+
+ AssertSql(
+"""
+@__p_1='50'
+@__p_0='10'
+@__p_2='100'
+
+SELECT `o0`.`CustomerID` AS `Key`, AVG(CAST(`o0`.`OrderID` AS double)) AS `Count`
+FROM (
+ SELECT `c`.`CustomerID`
+ FROM `Customers` AS `c`
+ WHERE `c`.`CustomerID` NOT IN ('DRACD', 'FOLKO')
+ ORDER BY `c`.`City`
+ LIMIT @__p_1 OFFSET @__p_0
+) AS `c0`
+INNER JOIN (
+ SELECT `o`.`OrderID`, `o`.`CustomerID`
+ FROM `Orders` AS `o`
+ WHERE `o`.`OrderID` < 10400
+ ORDER BY `o`.`OrderDate`
+ LIMIT @__p_2
+) AS `o0` ON `c0`.`CustomerID` = `o0`.`CustomerID`
+WHERE `o0`.`OrderID` > 10300
+GROUP BY `o0`.`CustomerID`
+""");
+ }
+
+ public override async Task Self_join_GroupBy_Aggregate(bool async)
+ {
+ await base.Self_join_GroupBy_Aggregate(async);
+
+ AssertSql(
+"""
+SELECT `o`.`CustomerID` AS `Key`, AVG(CAST(`o0`.`OrderID` AS double)) AS `Count`
+FROM `Orders` AS `o`
+INNER JOIN `Orders` AS `o0` ON `o`.`OrderID` = `o0`.`OrderID`
+WHERE `o`.`OrderID` < 10400
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_multi_navigation_members_Aggregate(bool async)
+ {
+ await base.GroupBy_multi_navigation_members_Aggregate(async);
+
+ AssertSql(
+"""
+SELECT `o0`.`CustomerID`, `p`.`ProductName`, COUNT(*) AS `Count`
+FROM `Order Details` AS `o`
+INNER JOIN `Orders` AS `o0` ON `o`.`OrderID` = `o0`.`OrderID`
+INNER JOIN `Products` AS `p` ON `o`.`ProductID` = `p`.`ProductID`
+GROUP BY `o0`.`CustomerID`, `p`.`ProductName`
+""");
+ }
+
+ public override async Task Union_simple_groupby(bool async)
+ {
+ await base.Union_simple_groupby(async);
+
+ AssertSql(
+"""
+SELECT `u`.`City` AS `Key`, COUNT(*) AS `Total`
+FROM (
+ 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`.`ContactTitle` = 'Owner'
+ UNION
+ SELECT `c0`.`CustomerID`, `c0`.`Address`, `c0`.`City`, `c0`.`CompanyName`, `c0`.`ContactName`, `c0`.`ContactTitle`, `c0`.`Country`, `c0`.`Fax`, `c0`.`Phone`, `c0`.`PostalCode`, `c0`.`Region`
+ FROM `Customers` AS `c0`
+ WHERE `c0`.`City` = 'México D.F.'
+) AS `u`
+GROUP BY `u`.`City`
+""");
+ }
+
+ public override async Task Select_anonymous_GroupBy_Aggregate(bool async)
+ {
+ await base.Select_anonymous_GroupBy_Aggregate(async);
+
+ AssertSql(
+"""
+SELECT MIN(`o`.`OrderDate`) AS `Min`, MAX(`o`.`OrderDate`) AS `Max`, COALESCE(SUM(`o`.`OrderID`), 0) AS `Sum`, AVG(CAST(`o`.`OrderID` AS double)) AS `Avg`
+FROM `Orders` AS `o`
+WHERE `o`.`OrderID` < 10300
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_principal_key_property_optimization(bool async)
+ {
+ await base.GroupBy_principal_key_property_optimization(async);
+
+ AssertSql(
+"""
+SELECT `c`.`CustomerID` AS `Key`, COUNT(*) AS `Count`
+FROM `Orders` AS `o`
+LEFT JOIN `Customers` AS `c` ON `o`.`CustomerID` = `c`.`CustomerID`
+GROUP BY `c`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_after_anonymous_projection_and_distinct_followed_by_another_anonymous_projection(bool async)
+ {
+ await base.GroupBy_after_anonymous_projection_and_distinct_followed_by_another_anonymous_projection(async);
+
+ AssertSql(
+"""
+SELECT `o0`.`CustomerID` AS `Key`, COUNT(*) AS `Count`
+FROM (
+ SELECT DISTINCT `o`.`CustomerID`, `o`.`OrderID`
+ FROM `Orders` AS `o`
+) AS `o0`
+GROUP BY `o0`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_complex_key_aggregate(bool async)
+ {
+ await base.GroupBy_complex_key_aggregate(async);
+
+ AssertSql(
+"""
+SELECT `s`.`Key`, COUNT(*) AS `Count`
+FROM (
+ SELECT SUBSTRING(`c`.`CustomerID`, 0 + 1, 1) AS `Key`
+ FROM `Orders` AS `o`
+ LEFT JOIN `Customers` AS `c` ON `o`.`CustomerID` = `c`.`CustomerID`
+) AS `s`
+GROUP BY `s`.`Key`
+""");
+ }
+
+ public override async Task GroupBy_complex_key_aggregate_2(bool async)
+ {
+ await base.GroupBy_complex_key_aggregate_2(async);
+
+ AssertSql(
+"""
+SELECT `o0`.`Key` AS `Month`, COALESCE(SUM(`o0`.`OrderID`), 0) AS `Total`, (
+ SELECT COALESCE(SUM(`o1`.`OrderID`), 0)
+ FROM `Orders` AS `o1`
+ WHERE (EXTRACT(month FROM `o1`.`OrderDate`) = `o0`.`Key`) OR (EXTRACT(month FROM `o1`.`OrderDate`) IS NULL AND (`o0`.`Key` IS NULL))) AS `Payment`
+FROM (
+ SELECT `o`.`OrderID`, EXTRACT(month FROM `o`.`OrderDate`) AS `Key`
+ FROM `Orders` AS `o`
+) AS `o0`
+GROUP BY `o0`.`Key`
+""");
+ }
+
+ public override async Task Select_collection_of_scalar_before_GroupBy_aggregate(bool async)
+ {
+ await base.Select_collection_of_scalar_before_GroupBy_aggregate(async);
+
+ AssertSql(
+"""
+SELECT `c`.`City` AS `Key`, COUNT(*) AS `Count`
+FROM `Customers` AS `c`
+GROUP BY `c`.`City`
+""");
+ }
+
+ public override async Task GroupBy_OrderBy_key(bool async)
+ {
+ await base.GroupBy_OrderBy_key(async);
+
+ AssertSql(
+"""
+SELECT `o`.`CustomerID` AS `Key`, COUNT(*) AS `c`
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+ORDER BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_OrderBy_count(bool async)
+ {
+ await base.GroupBy_OrderBy_count(async);
+
+ AssertSql(
+"""
+SELECT `o`.`CustomerID` AS `Key`, COUNT(*) AS `Count`
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+ORDER BY COUNT(*), `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_OrderBy_count_Select_sum(bool async)
+ {
+ await base.GroupBy_OrderBy_count_Select_sum(async);
+
+ AssertSql(
+"""
+SELECT `o`.`CustomerID` AS `Key`, COALESCE(SUM(`o`.`OrderID`), 0) AS `Sum`
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+ORDER BY COUNT(*), `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_aggregate_Contains(bool async)
+ {
+ await base.GroupBy_aggregate_Contains(async);
+
+ AssertSql(
+"""
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+FROM `Orders` AS `o`
+WHERE EXISTS (
+ SELECT 1
+ FROM `Orders` AS `o0`
+ GROUP BY `o0`.`CustomerID`
+ HAVING (COUNT(*) > 30) AND ((`o0`.`CustomerID` = `o`.`CustomerID`) OR (`o0`.`CustomerID` IS NULL AND (`o`.`CustomerID` IS NULL))))
+""");
+ }
+
+ public override async Task GroupBy_aggregate_Pushdown(bool async)
+ {
+ await base.GroupBy_aggregate_Pushdown(async);
+
+ AssertSql(
+"""
+@__p_0='20'
+@__p_1='4'
+
+SELECT `o0`.`CustomerID`
+FROM (
+ SELECT `o`.`CustomerID`
+ FROM `Orders` AS `o`
+ GROUP BY `o`.`CustomerID`
+ HAVING COUNT(*) > 10
+ ORDER BY `o`.`CustomerID`
+ LIMIT @__p_0
+) AS `o0`
+ORDER BY `o0`.`CustomerID`
+LIMIT 18446744073709551610 OFFSET @__p_1
+""");
+ }
+
+ public override async Task GroupBy_aggregate_using_grouping_key_Pushdown(bool async)
+ {
+ await base.GroupBy_aggregate_using_grouping_key_Pushdown(async);
+
+ AssertSql(
+"""
+@__p_0='20'
+@__p_1='4'
+
+SELECT `o0`.`Key`, `o0`.`Max`
+FROM (
+ SELECT `o`.`CustomerID` AS `Key`, MAX(`o`.`CustomerID`) AS `Max`
+ FROM `Orders` AS `o`
+ GROUP BY `o`.`CustomerID`
+ HAVING COUNT(*) > 10
+ ORDER BY `o`.`CustomerID`
+ LIMIT @__p_0
+) AS `o0`
+ORDER BY `o0`.`Key`
+LIMIT 18446744073709551610 OFFSET @__p_1
+""");
+ }
+
+ public override async Task GroupBy_aggregate_Pushdown_followed_by_projecting_Length(bool async)
+ {
+ await base.GroupBy_aggregate_Pushdown_followed_by_projecting_Length(async);
+
+ AssertSql(
+"""
+@__p_0='20'
+@__p_1='4'
+
+SELECT CHAR_LENGTH(`o0`.`CustomerID`)
+FROM (
+ SELECT `o`.`CustomerID`
+ FROM `Orders` AS `o`
+ GROUP BY `o`.`CustomerID`
+ HAVING COUNT(*) > 10
+ ORDER BY `o`.`CustomerID`
+ LIMIT @__p_0
+) AS `o0`
+ORDER BY `o0`.`CustomerID`
+LIMIT 18446744073709551610 OFFSET @__p_1
+""");
+ }
+
+ public override async Task GroupBy_aggregate_Pushdown_followed_by_projecting_constant(bool async)
+ {
+ await base.GroupBy_aggregate_Pushdown_followed_by_projecting_constant(async);
+
+ AssertSql(
+"""
+@__p_0='20'
+@__p_1='4'
+
+SELECT 5
+FROM (
+ SELECT `o`.`CustomerID`
+ FROM `Orders` AS `o`
+ GROUP BY `o`.`CustomerID`
+ HAVING COUNT(*) > 10
+ ORDER BY `o`.`CustomerID`
+ LIMIT @__p_0
+) AS `o0`
+ORDER BY `o0`.`CustomerID`
+LIMIT 18446744073709551610 OFFSET @__p_1
+""");
+ }
+
+ public override async Task GroupBy_filter_key(bool async)
+ {
+ await base.GroupBy_filter_key(async);
+
+ AssertSql(
+"""
+SELECT `o0`.`Key`, `o0`.`c`
+FROM (
+ SELECT `o`.`CustomerID` AS `Key`, COUNT(*) AS `c`, (`o`.`CustomerID` = 'ALFKI') AND `o`.`CustomerID` IS NOT NULL AS `c0`
+ FROM `Orders` AS `o`
+ GROUP BY `o`.`CustomerID`, `c0`
+ HAVING `c0`
+) AS `o0`
+""");
+ }
+
+ public override async Task GroupBy_filter_count(bool async)
+ {
+ await base.GroupBy_filter_count(async);
+
+ AssertSql(
+"""
+SELECT `o`.`CustomerID` AS `Key`, COUNT(*) AS `Count`
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+HAVING COUNT(*) > 4
+""");
+ }
+
+ public override async Task GroupBy_count_filter(bool async)
+ {
+ await base.GroupBy_count_filter(async);
+
+ AssertSql(
+"""
+SELECT `o0`.`Key` AS `Name`, COUNT(*) AS `Count`
+FROM (
+ SELECT 'Order' AS `Key`
+ FROM `Orders` AS `o`
+) AS `o0`
+GROUP BY `o0`.`Key`
+HAVING COUNT(*) > 0
+""");
+ }
+
+ public override async Task GroupBy_filter_count_OrderBy_count_Select_sum(bool async)
+ {
+ await base.GroupBy_filter_count_OrderBy_count_Select_sum(async);
+
+ AssertSql(
+"""
+SELECT `o`.`CustomerID` AS `Key`, COUNT(*) AS `Count`, COALESCE(SUM(`o`.`OrderID`), 0) AS `Sum`
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+HAVING COUNT(*) > 4
+ORDER BY COUNT(*), `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_Aggregate_Join(bool async)
+ {
+ await base.GroupBy_Aggregate_Join(async);
+
+ AssertSql(
+"""
+SELECT `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`, `o1`.`OrderID`, `o1`.`CustomerID`, `o1`.`EmployeeID`, `o1`.`OrderDate`
+FROM (
+ SELECT `o`.`CustomerID`, MAX(`o`.`OrderID`) AS `LastOrderID`
+ FROM `Orders` AS `o`
+ GROUP BY `o`.`CustomerID`
+ HAVING COUNT(*) > 5
+) AS `o0`
+INNER JOIN `Customers` AS `c` ON `o0`.`CustomerID` = `c`.`CustomerID`
+INNER JOIN `Orders` AS `o1` ON `o0`.`LastOrderID` = `o1`.`OrderID`
+""");
+ }
+
+ public override async Task GroupBy_Aggregate_Join_converted_from_SelectMany(bool async)
+ {
+ await base.GroupBy_Aggregate_Join_converted_from_SelectMany(async);
+
+ AssertSql(
+"""
+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`
+INNER JOIN (
+ SELECT `o`.`CustomerID`
+ FROM `Orders` AS `o`
+ GROUP BY `o`.`CustomerID`
+ HAVING COUNT(*) > 5
+) AS `o0` ON `c`.`CustomerID` = `o0`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_Aggregate_LeftJoin_converted_from_SelectMany(bool async)
+ {
+ await base.GroupBy_Aggregate_LeftJoin_converted_from_SelectMany(async);
+
+ AssertSql(
+"""
+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`
+LEFT JOIN (
+ SELECT `o`.`CustomerID`
+ FROM `Orders` AS `o`
+ GROUP BY `o`.`CustomerID`
+ HAVING COUNT(*) > 5
+) AS `o0` ON `c`.`CustomerID` = `o0`.`CustomerID`
+""");
+ }
+
+ public override async Task Join_GroupBy_Aggregate_multijoins(bool async)
+ {
+ await base.Join_GroupBy_Aggregate_multijoins(async);
+
+ AssertSql(
+"""
+SELECT `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`, `o1`.`OrderID`, `o1`.`CustomerID`, `o1`.`EmployeeID`, `o1`.`OrderDate`
+FROM `Customers` AS `c`
+INNER JOIN (
+ SELECT `o`.`CustomerID`, MAX(`o`.`OrderID`) AS `LastOrderID`
+ FROM `Orders` AS `o`
+ GROUP BY `o`.`CustomerID`
+ HAVING COUNT(*) > 5
+) AS `o0` ON `c`.`CustomerID` = `o0`.`CustomerID`
+INNER JOIN `Orders` AS `o1` ON `o0`.`LastOrderID` = `o1`.`OrderID`
+""");
+ }
+
+ public override async Task Join_GroupBy_Aggregate_single_join(bool async)
+ {
+ await base.Join_GroupBy_Aggregate_single_join(async);
+
+ AssertSql(
+"""
+SELECT `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`, `o0`.`LastOrderID`
+FROM `Customers` AS `c`
+INNER JOIN (
+ SELECT `o`.`CustomerID`, MAX(`o`.`OrderID`) AS `LastOrderID`
+ FROM `Orders` AS `o`
+ GROUP BY `o`.`CustomerID`
+ HAVING COUNT(*) > 5
+) AS `o0` ON `c`.`CustomerID` = `o0`.`CustomerID`
+""");
+ }
+
+ public override async Task Join_GroupBy_Aggregate_with_another_join(bool async)
+ {
+ await base.Join_GroupBy_Aggregate_with_another_join(async);
+
+ AssertSql(
+"""
+SELECT `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`, `o0`.`LastOrderID`, `o1`.`OrderID`
+FROM `Customers` AS `c`
+INNER JOIN (
+ SELECT `o`.`CustomerID`, MAX(`o`.`OrderID`) AS `LastOrderID`
+ FROM `Orders` AS `o`
+ GROUP BY `o`.`CustomerID`
+ HAVING COUNT(*) > 5
+) AS `o0` ON `c`.`CustomerID` = `o0`.`CustomerID`
+INNER JOIN `Orders` AS `o1` ON `c`.`CustomerID` = `o1`.`CustomerID`
+""");
+ }
+
+ public override async Task Join_GroupBy_Aggregate_distinct_single_join(bool async)
+ {
+ await base.Join_GroupBy_Aggregate_distinct_single_join(async);
+
+ AssertSql(
+"""
+SELECT `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`, `o1`.`LastOrderID`
+FROM `Customers` AS `c`
+INNER JOIN (
+ SELECT DISTINCT `o0`.`CustomerID`, MAX(`o0`.`OrderID`) AS `LastOrderID`
+ FROM (
+ SELECT `o`.`OrderID`, `o`.`CustomerID`, EXTRACT(year FROM `o`.`OrderDate`) AS `Year`
+ FROM `Orders` AS `o`
+ ) AS `o0`
+ GROUP BY `o0`.`CustomerID`, `o0`.`Year`
+ HAVING COUNT(*) > 5
+) AS `o1` ON `c`.`CustomerID` = `o1`.`CustomerID`
+""");
+ }
+
+ public override async Task Join_GroupBy_Aggregate_with_left_join(bool async)
+ {
+ await base.Join_GroupBy_Aggregate_with_left_join(async);
+
+ AssertSql(
+"""
+SELECT `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`, `o0`.`LastOrderID`
+FROM `Customers` AS `c`
+LEFT JOIN (
+ SELECT `o`.`CustomerID`, MAX(`o`.`OrderID`) AS `LastOrderID`
+ FROM `Orders` AS `o`
+ GROUP BY `o`.`CustomerID`
+ HAVING COUNT(*) > 5
+) AS `o0` ON `c`.`CustomerID` = `o0`.`CustomerID`
+WHERE `c`.`CustomerID` LIKE 'A%'
+""");
+ }
+
+ public override async Task Join_GroupBy_Aggregate_in_subquery(bool async)
+ {
+ await base.Join_GroupBy_Aggregate_in_subquery(async);
+
+ AssertSql(
+"""
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`, `s`.`CustomerID`, `s`.`Address`, `s`.`City`, `s`.`CompanyName`, `s`.`ContactName`, `s`.`ContactTitle`, `s`.`Country`, `s`.`Fax`, `s`.`Phone`, `s`.`PostalCode`, `s`.`Region`
+FROM `Orders` AS `o`
+INNER JOIN (
+ 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`
+ INNER JOIN (
+ SELECT `o0`.`CustomerID`
+ FROM `Orders` AS `o0`
+ GROUP BY `o0`.`CustomerID`
+ HAVING COUNT(*) > 5
+ ) AS `o1` ON `c`.`CustomerID` = `o1`.`CustomerID`
+) AS `s` ON `o`.`CustomerID` = `s`.`CustomerID`
+WHERE `o`.`OrderID` < 10400
+""");
+ }
+
+ public override async Task Join_GroupBy_Aggregate_on_key(bool async)
+ {
+ await base.Join_GroupBy_Aggregate_on_key(async);
+
+ AssertSql(
+"""
+SELECT `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`, `o0`.`LastOrderID`
+FROM `Customers` AS `c`
+INNER JOIN (
+ SELECT `o`.`CustomerID` AS `Key`, MAX(`o`.`OrderID`) AS `LastOrderID`
+ FROM `Orders` AS `o`
+ GROUP BY `o`.`CustomerID`
+ HAVING COUNT(*) > 5
+) AS `o0` ON `c`.`CustomerID` = `o0`.`Key`
+""");
+ }
+
+ public override async Task GroupBy_with_result_selector(bool async)
+ {
+ await base.GroupBy_with_result_selector(async);
+
+ AssertSql(
+"""
+SELECT COALESCE(SUM(`o`.`OrderID`), 0) AS `Sum`, MIN(`o`.`OrderID`) AS `Min`, MAX(`o`.`OrderID`) AS `Max`, AVG(CAST(`o`.`OrderID` AS double)) AS `Avg`
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_Sum_constant(bool async)
+ {
+ await base.GroupBy_Sum_constant(async);
+
+ AssertSql(
+"""
+SELECT COALESCE(SUM(1), 0)
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_Sum_constant_cast(bool async)
+ {
+ await base.GroupBy_Sum_constant_cast(async);
+
+ AssertSql(
+"""
+SELECT COALESCE(SUM(1), 0)
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task Distinct_GroupBy_OrderBy_key(bool async)
+ {
+ await base.Distinct_GroupBy_OrderBy_key(async);
+
+ AssertSql(
+"""
+SELECT `o0`.`CustomerID` AS `Key`, COUNT(*) AS `c`
+FROM (
+ SELECT DISTINCT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ FROM `Orders` AS `o`
+) AS `o0`
+GROUP BY `o0`.`CustomerID`
+ORDER BY `o0`.`CustomerID`
+""");
+ }
+
+ public override async Task Select_uncorrelated_collection_with_groupby_works(bool async)
+ {
+ await base.Select_uncorrelated_collection_with_groupby_works(async);
+
+ AssertSql(
+"""
+SELECT `c`.`CustomerID`, `o0`.`OrderID`
+FROM `Customers` AS `c`
+LEFT JOIN LATERAL (
+ SELECT `o`.`OrderID`
+ FROM `Orders` AS `o`
+ GROUP BY `o`.`OrderID`
+) AS `o0` ON TRUE
+WHERE `c`.`CustomerID` LIKE 'A%'
+ORDER BY `c`.`CustomerID`
+""");
+ }
+
+ public override async Task Select_uncorrelated_collection_with_groupby_multiple_collections_work(bool async)
+ {
+ await base.Select_uncorrelated_collection_with_groupby_multiple_collections_work(async);
+
+ AssertSql(
+"""
+SELECT `o`.`OrderID`, `p1`.`ProductID`, `p2`.`c`, `p2`.`ProductID`
+FROM `Orders` AS `o`
+LEFT JOIN LATERAL (
+ SELECT `p`.`ProductID`
+ FROM `Products` AS `p`
+ GROUP BY `p`.`ProductID`
+) AS `p1` ON TRUE
+LEFT JOIN LATERAL (
+ SELECT COUNT(*) AS `c`, `p0`.`ProductID`
+ FROM `Products` AS `p0`
+ GROUP BY `p0`.`ProductID`
+) AS `p2` ON TRUE
+WHERE `o`.`CustomerID` LIKE 'A%'
+ORDER BY `o`.`OrderID`, `p1`.`ProductID`
+""");
+ }
+
+ public override async Task Select_GroupBy_All(bool async)
+ {
+ await base.Select_GroupBy_All(async);
+
+ AssertSql(
+"""
+SELECT NOT EXISTS (
+ SELECT 1
+ FROM (
+ SELECT (`o`.`CustomerID` <> 'ALFKI') OR `o`.`CustomerID` IS NULL AS `c`
+ FROM `Orders` AS `o`
+ GROUP BY `o`.`CustomerID`, `c`
+ HAVING `c`
+ ) AS `o0`)
+""");
+ }
+
+ public override async Task GroupBy_multiple_Count_with_predicate(bool async)
+ {
+ await base.GroupBy_multiple_Count_with_predicate(async);
+
+ AssertSql(
+"""
+SELECT `o`.`CustomerID`, COUNT(*) AS `All`, COUNT(CASE
+ WHEN `o`.`OrderID` < 11000 THEN 1
+END) AS `TenK`, COUNT(CASE
+ WHEN `o`.`OrderID` < 12000 THEN 1
+END) AS `EleventK`
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_multiple_Sum_with_conditional_projection(bool async)
+ {
+ await base.GroupBy_multiple_Sum_with_conditional_projection(async);
+
+ AssertSql(
+"""
+SELECT `o`.`CustomerID`, COALESCE(SUM(CASE
+ WHEN `o`.`OrderID` < 11000 THEN `o`.`OrderID`
+ ELSE 0
+END), 0) AS `TenK`, COALESCE(SUM(CASE
+ WHEN `o`.`OrderID` >= 11000 THEN `o`.`OrderID`
+ ELSE 0
+END), 0) AS `EleventK`
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_Key_as_part_of_element_selector(bool async)
+ {
+ await base.GroupBy_Key_as_part_of_element_selector(async);
+
+ AssertSql(
+"""
+SELECT `o`.`OrderID` AS `Key`, AVG(CAST(`o`.`OrderID` AS double)) AS `Avg`, MAX(`o`.`OrderDate`) AS `Max`
+FROM `Orders` AS `o`
+GROUP BY `o`.`OrderID`
+""");
+ }
+
+ public override async Task GroupBy_composite_Key_as_part_of_element_selector(bool async)
+ {
+ await base.GroupBy_composite_Key_as_part_of_element_selector(async);
+
+ AssertSql(
+"""
+SELECT `o`.`OrderID`, `o`.`CustomerID`, AVG(CAST(`o`.`OrderID` AS double)) AS `Avg`, MAX(`o`.`OrderDate`) AS `Max`
+FROM `Orders` AS `o`
+GROUP BY `o`.`OrderID`, `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_with_order_by_skip_and_another_order_by(bool async)
+ {
+ await base.GroupBy_with_order_by_skip_and_another_order_by(async);
+
+ AssertSql(
+"""
+@__p_0='80'
+
+SELECT COALESCE(SUM(`o0`.`OrderID`), 0)
+FROM (
+ SELECT `o`.`OrderID`, `o`.`CustomerID`
+ FROM `Orders` AS `o`
+ ORDER BY `o`.`CustomerID`, `o`.`OrderID`
+ LIMIT 18446744073709551610 OFFSET @__p_0
+) AS `o0`
+GROUP BY `o0`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_Property_Select_Count_with_predicate(bool async)
+ {
+ await base.GroupBy_Property_Select_Count_with_predicate(async);
+
+ AssertSql(
+"""
+SELECT COUNT(CASE
+ WHEN `o`.`OrderID` < 10300 THEN 1
+END)
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_Property_Select_LongCount_with_predicate(bool async)
+ {
+ await base.GroupBy_Property_Select_LongCount_with_predicate(async);
+
+ AssertSql(
+"""
+SELECT COUNT(CASE
+ WHEN `o`.`OrderID` < 10300 THEN 1
+END)
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_orderby_projection_with_coalesce_operation(bool async)
+ {
+ await base.GroupBy_orderby_projection_with_coalesce_operation(async);
+
+ AssertSql(
+"""
+SELECT COALESCE(`c`.`City`, 'Unknown') AS `Locality`, COUNT(*) AS `Count`
+FROM `Customers` AS `c`
+GROUP BY `c`.`City`
+ORDER BY COUNT(*) DESC, `c`.`City`
+""");
+ }
+
+ public override async Task GroupBy_let_orderby_projection_with_coalesce_operation(bool async)
+ {
+ await base.GroupBy_let_orderby_projection_with_coalesce_operation(async);
+
+ AssertSql();
+ }
+
+ public override async Task GroupBy_Min_Where_optional_relationship(bool async)
+ {
+ await base.GroupBy_Min_Where_optional_relationship(async);
+
+ AssertSql(
+"""
+SELECT `c`.`CustomerID` AS `Key`, COUNT(*) AS `Count`
+FROM `Orders` AS `o`
+LEFT JOIN `Customers` AS `c` ON `o`.`CustomerID` = `c`.`CustomerID`
+GROUP BY `c`.`CustomerID`
+HAVING COUNT(*) <> 2
+""");
+ }
+
+ public override async Task GroupBy_Min_Where_optional_relationship_2(bool async)
+ {
+ await base.GroupBy_Min_Where_optional_relationship_2(async);
+
+ AssertSql(
+"""
+SELECT `c`.`CustomerID` AS `Key`, COUNT(*) AS `Count`
+FROM `Orders` AS `o`
+LEFT JOIN `Customers` AS `c` ON `o`.`CustomerID` = `c`.`CustomerID`
+GROUP BY `c`.`CustomerID`
+HAVING (COUNT(*) < 2) OR (COUNT(*) > 2)
+""");
+ }
+
+ public override async Task GroupBy_aggregate_over_a_subquery(bool async)
+ {
+ await base.GroupBy_aggregate_over_a_subquery(async);
+
+ AssertSql(
+"""
+SELECT `o`.`CustomerID` AS `Key`, (
+ SELECT COUNT(*)
+ FROM `Customers` AS `c`
+ WHERE `c`.`CustomerID` = `o`.`CustomerID`) AS `Count`
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_aggregate_join_with_grouping_key(bool async)
+ {
+ await base.GroupBy_aggregate_join_with_grouping_key(async);
+
+ AssertSql(
+"""
+SELECT `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`, `o0`.`Count`
+FROM (
+ SELECT `o`.`CustomerID` AS `Key`, COUNT(*) AS `Count`
+ FROM `Orders` AS `o`
+ GROUP BY `o`.`CustomerID`
+) AS `o0`
+INNER JOIN `Customers` AS `c` ON `o0`.`Key` = `c`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_aggregate_join_with_group_result(bool async)
+ {
+ await base.GroupBy_aggregate_join_with_group_result(async);
+
+ AssertSql(
+"""
+SELECT `o0`.`OrderID`, `o0`.`CustomerID`, `o0`.`EmployeeID`, `o0`.`OrderDate`
+FROM (
+ SELECT `o`.`CustomerID` AS `Key`, MAX(`o`.`OrderDate`) AS `LastOrderDate`
+ FROM `Orders` AS `o`
+ GROUP BY `o`.`CustomerID`
+) AS `o1`
+INNER JOIN `Orders` AS `o0` ON ((`o1`.`Key` = `o0`.`CustomerID`) OR (`o1`.`Key` IS NULL AND (`o0`.`CustomerID` IS NULL))) AND ((`o1`.`LastOrderDate` = `o0`.`OrderDate`) OR (`o1`.`LastOrderDate` IS NULL AND (`o0`.`OrderDate` IS NULL)))
+""");
+ }
+
+ public override async Task GroupBy_aggregate_from_right_side_of_join(bool async)
+ {
+ await base.GroupBy_aggregate_from_right_side_of_join(async);
+
+ AssertSql(
+"""
+@__p_0='10'
+
+SELECT `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`, `o0`.`Max`
+FROM `Customers` AS `c`
+INNER JOIN (
+ SELECT `o`.`CustomerID` AS `Key`, MAX(`o`.`OrderDate`) AS `Max`
+ FROM `Orders` AS `o`
+ GROUP BY `o`.`CustomerID`
+) AS `o0` ON `c`.`CustomerID` = `o0`.`Key`
+ORDER BY `o0`.`Max`, `c`.`CustomerID`
+LIMIT @__p_0 OFFSET @__p_0
+""");
+ }
+
+ public override async Task GroupBy_aggregate_join_another_GroupBy_aggregate(bool async)
+ {
+ await base.GroupBy_aggregate_join_another_GroupBy_aggregate(async);
+
+ AssertSql(
+"""
+SELECT `o1`.`Key`, `o1`.`Total`, `o2`.`ThatYear`
+FROM (
+ SELECT `o`.`CustomerID` AS `Key`, COUNT(*) AS `Total`
+ FROM `Orders` AS `o`
+ GROUP BY `o`.`CustomerID`
+) AS `o1`
+INNER JOIN (
+ SELECT `o0`.`CustomerID` AS `Key`, COUNT(*) AS `ThatYear`
+ FROM `Orders` AS `o0`
+ WHERE EXTRACT(year FROM `o0`.`OrderDate`) = 1997
+ GROUP BY `o0`.`CustomerID`
+) AS `o2` ON `o1`.`Key` = `o2`.`Key`
+""");
+ }
+
+ public override async Task GroupBy_aggregate_after_skip_0_take_0(bool async)
+ {
+ await base.GroupBy_aggregate_after_skip_0_take_0(async);
+
+ AssertSql(
+ AppConfig.ServerVersion.Supports.MySqlBugLimit0Offset0ExistsWorkaround
+ ? """
+SELECT `o0`.`CustomerID` AS `Key`, COUNT(*) AS `Total`
+FROM (
+ SELECT `o`.`CustomerID`
+ FROM `Orders` AS `o`
+ WHERE FALSE
+) AS `o0`
+GROUP BY `o0`.`CustomerID`
+"""
+ : """
+@__p_0='0'
+
+SELECT `o0`.`CustomerID` AS `Key`, COUNT(*) AS `Total`
+FROM (
+ SELECT `o`.`CustomerID`
+ FROM `Orders` AS `o`
+ LIMIT @__p_0 OFFSET @__p_0
+) AS `o0`
+GROUP BY `o0`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_skip_0_take_0_aggregate(bool async)
+ {
+ await base.GroupBy_skip_0_take_0_aggregate(async);
+
+ AssertSql(
+ AppConfig.ServerVersion.Supports.MySqlBugLimit0Offset0ExistsWorkaround
+ ? """
+SELECT `o`.`CustomerID` AS `Key`, COUNT(*) AS `Total`
+FROM `Orders` AS `o`
+WHERE `o`.`OrderID` > 10500
+GROUP BY `o`.`CustomerID`
+HAVING FALSE
+"""
+ : """
+@__p_0='0'
+
+SELECT `o`.`CustomerID` AS `Key`, COUNT(*) AS `Total`
+FROM `Orders` AS `o`
+WHERE `o`.`OrderID` > 10500
+GROUP BY `o`.`CustomerID`
+LIMIT @__p_0 OFFSET @__p_0
+""");
+ }
+
+ public override async Task GroupBy_aggregate_followed_another_GroupBy_aggregate(bool async)
+ {
+ await base.GroupBy_aggregate_followed_another_GroupBy_aggregate(async);
+
+ AssertSql(
+"""
+SELECT `o1`.`CustomerID` AS `Key`, COUNT(*) AS `Count`
+FROM (
+ SELECT `o0`.`CustomerID`
+ FROM (
+ SELECT `o`.`CustomerID`, EXTRACT(year FROM `o`.`OrderDate`) AS `Year`
+ FROM `Orders` AS `o`
+ ) AS `o0`
+ GROUP BY `o0`.`CustomerID`, `o0`.`Year`
+) AS `o1`
+GROUP BY `o1`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_aggregate_SelectMany(bool async)
+ {
+ await base.GroupBy_aggregate_SelectMany(async);
+
+ AssertSql();
+ }
+
+ public override async Task GroupBy_aggregate_without_selectMany_selecting_first(bool async)
+ {
+ await base.GroupBy_aggregate_without_selectMany_selecting_first(async);
+
+ AssertSql(
+"""
+SELECT `o0`.`OrderID`, `o0`.`CustomerID`, `o0`.`EmployeeID`, `o0`.`OrderDate`
+FROM (
+ SELECT MIN(`o`.`OrderID`) AS `c`
+ FROM `Orders` AS `o`
+ GROUP BY `o`.`CustomerID`
+) AS `o1`
+CROSS JOIN `Orders` AS `o0`
+WHERE `o0`.`OrderID` = `o1`.`c`
+""");
+ }
+
+ public override async Task GroupBy_aggregate_left_join_GroupBy_aggregate_left_join(bool async)
+ {
+ await base.GroupBy_aggregate_left_join_GroupBy_aggregate_left_join(async);
+
+ AssertSql();
+ }
+
+ public override async Task GroupBy_Where_Average(bool async)
+ {
+ await base.GroupBy_Where_Average(async);
+
+ AssertSql(
+"""
+SELECT AVG(CASE
+ WHEN `o`.`OrderID` < 10300 THEN CAST(`o`.`OrderID` AS double)
+END)
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_Where_Count(bool async)
+ {
+ await base.GroupBy_Where_Count(async);
+
+ AssertSql(
+"""
+SELECT COUNT(CASE
+ WHEN `o`.`OrderID` < 10300 THEN 1
+END)
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_Where_LongCount(bool async)
+ {
+ await base.GroupBy_Where_LongCount(async);
+
+ AssertSql(
+"""
+SELECT COUNT(CASE
+ WHEN `o`.`OrderID` < 10300 THEN 1
+END)
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_Where_Max(bool async)
+ {
+ await base.GroupBy_Where_Max(async);
+
+ AssertSql(
+"""
+SELECT MAX(CASE
+ WHEN `o`.`OrderID` < 10300 THEN `o`.`OrderID`
+END)
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_Where_Min(bool async)
+ {
+ await base.GroupBy_Where_Min(async);
+
+ AssertSql(
+"""
+SELECT MIN(CASE
+ WHEN `o`.`OrderID` < 10300 THEN `o`.`OrderID`
+END)
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_Where_Sum(bool async)
+ {
+ await base.GroupBy_Where_Sum(async);
+
+ AssertSql(
+"""
+SELECT COALESCE(SUM(CASE
+ WHEN `o`.`OrderID` < 10300 THEN `o`.`OrderID`
+END), 0)
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_Where_Count_with_predicate(bool async)
+ {
+ await base.GroupBy_Where_Count_with_predicate(async);
+
+ AssertSql(
+"""
+SELECT COUNT(CASE
+ WHEN (`o`.`OrderID` < 10300) AND (`o`.`OrderDate` IS NOT NULL AND (EXTRACT(year FROM `o`.`OrderDate`) = 1997)) THEN 1
+END)
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_Where_Where_Count(bool async)
+ {
+ await base.GroupBy_Where_Where_Count(async);
+
+ AssertSql(
+"""
+SELECT COUNT(CASE
+ WHEN (`o`.`OrderID` < 10300) AND (`o`.`OrderDate` IS NOT NULL AND (EXTRACT(year FROM `o`.`OrderDate`) = 1997)) THEN 1
+END)
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_Where_Select_Where_Count(bool async)
+ {
+ await base.GroupBy_Where_Select_Where_Count(async);
+
+ AssertSql(
+"""
+SELECT COUNT(CASE
+ WHEN (`o`.`OrderID` < 10300) AND (`o`.`OrderDate` IS NOT NULL AND (EXTRACT(year FROM `o`.`OrderDate`) = 1997)) THEN 1
+END)
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_Where_Select_Where_Select_Min(bool async)
+ {
+ await base.GroupBy_Where_Select_Where_Select_Min(async);
+
+ AssertSql(
+"""
+SELECT MIN(CASE
+ WHEN (`o`.`OrderID` < 10300) AND (`o`.`OrderDate` IS NOT NULL AND (EXTRACT(year FROM `o`.`OrderDate`) = 1997)) THEN `o`.`OrderID`
+END)
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_multiple_Sum_with_Select_conditional_projection(bool async)
+ {
+ await base.GroupBy_multiple_Sum_with_Select_conditional_projection(async);
+
+ AssertSql(
+"""
+SELECT `o`.`CustomerID`, COALESCE(SUM(CASE
+ WHEN `o`.`OrderID` < 11000 THEN `o`.`OrderID`
+ ELSE 0
+END), 0) AS `TenK`, COALESCE(SUM(CASE
+ WHEN `o`.`OrderID` >= 11000 THEN `o`.`OrderID`
+ ELSE 0
+END), 0) AS `EleventK`
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task LongCount_after_GroupBy_aggregate(bool async)
+ {
+ await base.LongCount_after_GroupBy_aggregate(async);
+
+ AssertSql(
+"""
+SELECT COUNT(*)
+FROM (
+ SELECT 1
+ FROM `Orders` AS `o`
+ GROUP BY `o`.`CustomerID`
+) AS `o0`
+""");
+ }
+
+ public override async Task GroupBy_Select_Distinct_aggregate(bool async)
+ {
+ await base.GroupBy_Select_Distinct_aggregate(async);
+
+ AssertSql(
+"""
+SELECT `o`.`CustomerID` AS `Key`, AVG(DISTINCT (CAST(`o`.`OrderID` AS double))) AS `Average`, COUNT(DISTINCT (`o`.`EmployeeID`)) AS `Count`, COUNT(DISTINCT (`o`.`EmployeeID`)) AS `LongCount`, MAX(DISTINCT (`o`.`OrderDate`)) AS `Max`, MIN(DISTINCT (`o`.`OrderDate`)) AS `Min`, COALESCE(SUM(DISTINCT (`o`.`OrderID`)), 0) AS `Sum`
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task Final_GroupBy_property_entity(bool async)
+ {
+ await base.Final_GroupBy_property_entity(async);
+
+ AssertSql(
+"""
+SELECT `c`.`City`, `c`.`CustomerID`, `c`.`Address`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`
+FROM `Customers` AS `c`
+ORDER BY `c`.`City`
+""");
+ }
+
+ public override async Task Final_GroupBy_entity(bool async)
+ {
+ await base.Final_GroupBy_entity(async);
+
+ AssertSql(
+"""
+SELECT `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`, `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+FROM `Orders` AS `o`
+LEFT JOIN `Customers` AS `c` ON `o`.`CustomerID` = `c`.`CustomerID`
+WHERE `o`.`OrderID` < 10500
+ORDER BY `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`
+""");
+ }
+
+ public override async Task Final_GroupBy_property_entity_non_nullable(bool async)
+ {
+ await base.Final_GroupBy_property_entity_non_nullable(async);
+
+ AssertSql(
+"""
+SELECT `o`.`OrderID`, `o`.`ProductID`, `o`.`Discount`, `o`.`Quantity`, `o`.`UnitPrice`
+FROM `Order Details` AS `o`
+WHERE `o`.`OrderID` < 10500
+ORDER BY `o`.`OrderID`
+""");
+ }
+
+ public override async Task Final_GroupBy_property_anonymous_type(bool async)
+ {
+ await base.Final_GroupBy_property_anonymous_type(async);
+
+ AssertSql(
+"""
+SELECT `c`.`City`, `c`.`ContactName`, `c`.`ContactTitle`
+FROM `Customers` AS `c`
+ORDER BY `c`.`City`
+""");
+ }
+
+ public override async Task Final_GroupBy_multiple_properties_entity(bool async)
+ {
+ await base.Final_GroupBy_multiple_properties_entity(async);
+
+ AssertSql(
+"""
+SELECT `c`.`City`, `c`.`Region`, `c`.`CustomerID`, `c`.`Address`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`
+FROM `Customers` AS `c`
+ORDER BY `c`.`City`, `c`.`Region`
+""");
+ }
+
+ public override async Task Final_GroupBy_complex_key_entity(bool async)
+ {
+ await base.Final_GroupBy_complex_key_entity(async);
+
+ AssertSql(
+"""
+SELECT `c0`.`City`, `c0`.`Region`, `c0`.`Constant`, `c0`.`CustomerID`, `c0`.`Address`, `c0`.`CompanyName`, `c0`.`ContactName`, `c0`.`ContactTitle`, `c0`.`Country`, `c0`.`Fax`, `c0`.`Phone`, `c0`.`PostalCode`
+FROM (
+ SELECT `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`, 1 AS `Constant`
+ FROM `Customers` AS `c`
+) AS `c0`
+ORDER BY `c0`.`City`, `c0`.`Region`, `c0`.`Constant`
+""");
+ }
+
+ public override async Task Final_GroupBy_nominal_type_entity(bool async)
+ {
+ await base.Final_GroupBy_nominal_type_entity(async);
+
+ AssertSql(
+"""
+SELECT `c0`.`City`, `c0`.`Constant`, `c0`.`CustomerID`, `c0`.`Address`, `c0`.`CompanyName`, `c0`.`ContactName`, `c0`.`ContactTitle`, `c0`.`Country`, `c0`.`Fax`, `c0`.`Phone`, `c0`.`PostalCode`, `c0`.`Region`
+FROM (
+ SELECT `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`, 1 AS `Constant`
+ FROM `Customers` AS `c`
+) AS `c0`
+ORDER BY `c0`.`City`, `c0`.`Constant`
+""");
+ }
+
+ public override async Task Final_GroupBy_property_anonymous_type_element_selector(bool async)
+ {
+ await base.Final_GroupBy_property_anonymous_type_element_selector(async);
+
+ AssertSql(
+"""
+SELECT `c`.`City`, `c`.`ContactName`, `c`.`ContactTitle`
+FROM `Customers` AS `c`
+ORDER BY `c`.`City`
+""");
+ }
+
+ public override async Task Final_GroupBy_property_entity_Include_collection(bool async)
+ {
+ await base.Final_GroupBy_property_entity_Include_collection(async);
+
+ AssertSql(
+"""
+SELECT `c`.`City`, `c`.`CustomerID`, `c`.`Address`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`, `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+FROM `Customers` AS `c`
+LEFT JOIN `Orders` AS `o` ON `c`.`CustomerID` = `o`.`CustomerID`
+WHERE `c`.`Country` = 'USA'
+ORDER BY `c`.`City`, `c`.`CustomerID`
+""");
+ }
+
+ public override async Task Final_GroupBy_property_entity_projecting_collection(bool async)
+ {
+ await base.Final_GroupBy_property_entity_projecting_collection(async);
+
+ AssertSql(
+"""
+SELECT `c`.`City`, `c`.`CustomerID`, `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+FROM `Customers` AS `c`
+LEFT JOIN `Orders` AS `o` ON `c`.`CustomerID` = `o`.`CustomerID`
+WHERE `c`.`Country` = 'USA'
+ORDER BY `c`.`City`, `c`.`CustomerID`
+""");
+ }
+
+ public override async Task Final_GroupBy_property_entity_projecting_collection_composed(bool async)
+ {
+ await base.Final_GroupBy_property_entity_projecting_collection_composed(async);
+
+ AssertSql(
+"""
+SELECT `c`.`City`, `c`.`CustomerID`, `o0`.`OrderID`, `o0`.`CustomerID`, `o0`.`EmployeeID`, `o0`.`OrderDate`
+FROM `Customers` AS `c`
+LEFT JOIN (
+ SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ FROM `Orders` AS `o`
+ WHERE `o`.`OrderID` < 11000
+) AS `o0` ON `c`.`CustomerID` = `o0`.`CustomerID`
+WHERE `c`.`Country` = 'USA'
+ORDER BY `c`.`City`, `c`.`CustomerID`
+""");
+ }
+
+ public override async Task Final_GroupBy_property_entity_projecting_collection_and_single_result(bool async)
+ {
+ await base.Final_GroupBy_property_entity_projecting_collection_and_single_result(async);
+
+ AssertSql(
+"""
+SELECT `c`.`City`, `c`.`CustomerID`, `o1`.`OrderID`, `o1`.`CustomerID`, `o1`.`EmployeeID`, `o1`.`OrderDate`, `o3`.`OrderID`, `o3`.`CustomerID`, `o3`.`EmployeeID`, `o3`.`OrderDate`
+FROM `Customers` AS `c`
+LEFT JOIN (
+ SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ FROM `Orders` AS `o`
+ WHERE `o`.`OrderID` < 11000
+) AS `o1` ON `c`.`CustomerID` = `o1`.`CustomerID`
+LEFT JOIN (
+ SELECT `o2`.`OrderID`, `o2`.`CustomerID`, `o2`.`EmployeeID`, `o2`.`OrderDate`
+ FROM (
+ SELECT `o0`.`OrderID`, `o0`.`CustomerID`, `o0`.`EmployeeID`, `o0`.`OrderDate`, ROW_NUMBER() OVER(PARTITION BY `o0`.`CustomerID` ORDER BY `o0`.`OrderDate` DESC) AS `row`
+ FROM `Orders` AS `o0`
+ ) AS `o2`
+ WHERE `o2`.`row` <= 1
+) AS `o3` ON `c`.`CustomerID` = `o3`.`CustomerID`
+WHERE `c`.`Country` = 'USA'
+ORDER BY `c`.`City`, `c`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_Where_with_grouping_result(bool async)
+ {
+ await base.GroupBy_Where_with_grouping_result(async);
+
+ AssertSql();
+ }
+
+ public override async Task GroupBy_OrderBy_with_grouping_result(bool async)
+ {
+ await base.GroupBy_OrderBy_with_grouping_result(async);
+
+ AssertSql();
+ }
+
+ public override async Task GroupBy_SelectMany(bool async)
+ {
+ await base.GroupBy_SelectMany(async);
+
+ AssertSql();
+ }
+
+ public override async Task OrderBy_GroupBy_SelectMany(bool async)
+ {
+ await base.OrderBy_GroupBy_SelectMany(async);
+
+ AssertSql();
+ }
+
+ public override async Task OrderBy_GroupBy_SelectMany_shadow(bool async)
+ {
+ await base.OrderBy_GroupBy_SelectMany_shadow(async);
+
+ AssertSql();
+ }
+
+ public override async Task GroupBy_with_orderby_take_skip_distinct_followed_by_group_key_projection(bool async)
+ {
+ await base.GroupBy_with_orderby_take_skip_distinct_followed_by_group_key_projection(async);
+
+ AssertSql();
+ }
+
+ public override async Task GroupBy_Distinct(bool async)
+ {
+ await base.GroupBy_Distinct(async);
+
+ AssertSql();
+ }
+
+ public override async Task GroupBy_complex_key_without_aggregate(bool async)
+ {
+ await base.GroupBy_complex_key_without_aggregate(async);
+
+ AssertSql(
+"""
+SELECT `s1`.`Key`, `s3`.`OrderID`, `s3`.`CustomerID`, `s3`.`EmployeeID`, `s3`.`OrderDate`, `s3`.`CustomerID0`
+FROM (
+ SELECT `s`.`Key`
+ FROM (
+ SELECT SUBSTRING(`c`.`CustomerID`, 0 + 1, 1) AS `Key`
+ FROM `Orders` AS `o`
+ LEFT JOIN `Customers` AS `c` ON `o`.`CustomerID` = `c`.`CustomerID`
+ ) AS `s`
+ GROUP BY `s`.`Key`
+) AS `s1`
+LEFT JOIN (
+ SELECT `s2`.`OrderID`, `s2`.`CustomerID`, `s2`.`EmployeeID`, `s2`.`OrderDate`, `s2`.`CustomerID0`, `s2`.`Key`
+ FROM (
+ SELECT `s0`.`OrderID`, `s0`.`CustomerID`, `s0`.`EmployeeID`, `s0`.`OrderDate`, `s0`.`CustomerID0`, `s0`.`Key`, ROW_NUMBER() OVER(PARTITION BY `s0`.`Key` ORDER BY `s0`.`OrderID`, `s0`.`CustomerID0`) AS `row`
+ FROM (
+ SELECT `o0`.`OrderID`, `o0`.`CustomerID`, `o0`.`EmployeeID`, `o0`.`OrderDate`, `c0`.`CustomerID` AS `CustomerID0`, SUBSTRING(`c0`.`CustomerID`, 0 + 1, 1) AS `Key`
+ FROM `Orders` AS `o0`
+ LEFT JOIN `Customers` AS `c0` ON `o0`.`CustomerID` = `c0`.`CustomerID`
+ ) AS `s0`
+ ) AS `s2`
+ WHERE (1 < `s2`.`row`) AND (`s2`.`row` <= 3)
+) AS `s3` ON `s1`.`Key` = `s3`.`Key`
+ORDER BY `s1`.`Key`, `s3`.`OrderID`
+""");
+ }
+
+ public override async Task GroupBy_selecting_grouping_key_list(bool async)
+ {
+ await base.GroupBy_selecting_grouping_key_list(async);
+
+ AssertSql(
+"""
+SELECT `o1`.`CustomerID`, `o0`.`CustomerID`, `o0`.`OrderID`
+FROM (
+ SELECT `o`.`CustomerID`
+ FROM `Orders` AS `o`
+ GROUP BY `o`.`CustomerID`
+) AS `o1`
+LEFT JOIN `Orders` AS `o0` ON `o1`.`CustomerID` = `o0`.`CustomerID`
+ORDER BY `o1`.`CustomerID`
+""");
+ }
+
+ public override async Task Select_GroupBy_SelectMany(bool async)
+ {
+ await base.Select_GroupBy_SelectMany(async);
+
+ AssertSql();
+ }
+
+ public override async Task GroupBy_Shadow(bool async)
+ {
+ await base.GroupBy_Shadow(async);
+
+ AssertSql(
+"""
+SELECT (
+ SELECT `e0`.`Title`
+ FROM `Employees` AS `e0`
+ WHERE ((`e0`.`Title` = 'Sales Representative') AND (`e0`.`EmployeeID` = 1)) AND ((`e`.`Title` = `e0`.`Title`) OR (`e`.`Title` IS NULL AND (`e0`.`Title` IS NULL)))
+ LIMIT 1)
+FROM `Employees` AS `e`
+WHERE (`e`.`Title` = 'Sales Representative') AND (`e`.`EmployeeID` = 1)
+GROUP BY `e`.`Title`
+""");
+ }
+
+ public override async Task GroupBy_Shadow2(bool async)
+ {
+ await base.GroupBy_Shadow2(async);
+
+ AssertSql(
+"""
+SELECT `e3`.`EmployeeID`, `e3`.`City`, `e3`.`Country`, `e3`.`FirstName`, `e3`.`ReportsTo`, `e3`.`Title`
+FROM (
+ SELECT `e`.`Title`
+ FROM `Employees` AS `e`
+ WHERE (`e`.`Title` = 'Sales Representative') AND (`e`.`EmployeeID` = 1)
+ GROUP BY `e`.`Title`
+) AS `e1`
+LEFT JOIN (
+ SELECT `e2`.`EmployeeID`, `e2`.`City`, `e2`.`Country`, `e2`.`FirstName`, `e2`.`ReportsTo`, `e2`.`Title`
+ FROM (
+ SELECT `e0`.`EmployeeID`, `e0`.`City`, `e0`.`Country`, `e0`.`FirstName`, `e0`.`ReportsTo`, `e0`.`Title`, ROW_NUMBER() OVER(PARTITION BY `e0`.`Title` ORDER BY `e0`.`EmployeeID`) AS `row`
+ FROM `Employees` AS `e0`
+ WHERE (`e0`.`Title` = 'Sales Representative') AND (`e0`.`EmployeeID` = 1)
+ ) AS `e2`
+ WHERE `e2`.`row` <= 1
+) AS `e3` ON `e1`.`Title` = `e3`.`Title`
+""");
+ }
+
+ public override async Task GroupBy_Shadow3(bool async)
+ {
+ await base.GroupBy_Shadow3(async);
+
+ AssertSql(
+"""
+SELECT (
+ SELECT `e0`.`Title`
+ FROM `Employees` AS `e0`
+ WHERE (`e0`.`EmployeeID` = 1) AND (`e`.`EmployeeID` = `e0`.`EmployeeID`)
+ LIMIT 1)
+FROM `Employees` AS `e`
+WHERE `e`.`EmployeeID` = 1
+GROUP BY `e`.`EmployeeID`
+""");
+ }
+
+ public override async Task GroupBy_select_grouping_list(bool async)
+ {
+ await base.GroupBy_select_grouping_list(async);
+
+ AssertSql(
+"""
+SELECT `c1`.`City`, `c0`.`CustomerID`, `c0`.`Address`, `c0`.`City`, `c0`.`CompanyName`, `c0`.`ContactName`, `c0`.`ContactTitle`, `c0`.`Country`, `c0`.`Fax`, `c0`.`Phone`, `c0`.`PostalCode`, `c0`.`Region`
+FROM (
+ SELECT `c`.`City`
+ FROM `Customers` AS `c`
+ GROUP BY `c`.`City`
+) AS `c1`
+LEFT JOIN `Customers` AS `c0` ON `c1`.`City` = `c0`.`City`
+ORDER BY `c1`.`City`
+""");
+ }
+
+ public override async Task GroupBy_select_grouping_array(bool async)
+ {
+ await base.GroupBy_select_grouping_array(async);
+
+ AssertSql(
+"""
+SELECT `c1`.`City`, `c0`.`CustomerID`, `c0`.`Address`, `c0`.`City`, `c0`.`CompanyName`, `c0`.`ContactName`, `c0`.`ContactTitle`, `c0`.`Country`, `c0`.`Fax`, `c0`.`Phone`, `c0`.`PostalCode`, `c0`.`Region`
+FROM (
+ SELECT `c`.`City`
+ FROM `Customers` AS `c`
+ GROUP BY `c`.`City`
+) AS `c1`
+LEFT JOIN `Customers` AS `c0` ON `c1`.`City` = `c0`.`City`
+ORDER BY `c1`.`City`
+""");
+ }
+
+ public override async Task GroupBy_select_grouping_composed_list(bool async)
+ {
+ await base.GroupBy_select_grouping_composed_list(async);
+
+ AssertSql(
+"""
+SELECT `c1`.`City`, `c2`.`CustomerID`, `c2`.`Address`, `c2`.`City`, `c2`.`CompanyName`, `c2`.`ContactName`, `c2`.`ContactTitle`, `c2`.`Country`, `c2`.`Fax`, `c2`.`Phone`, `c2`.`PostalCode`, `c2`.`Region`
+FROM (
+ SELECT `c`.`City`
+ FROM `Customers` AS `c`
+ GROUP BY `c`.`City`
+) AS `c1`
+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`
+ FROM `Customers` AS `c0`
+ WHERE `c0`.`CustomerID` LIKE 'A%'
+) AS `c2` ON `c1`.`City` = `c2`.`City`
+ORDER BY `c1`.`City`
+""");
+ }
+
+ public override async Task GroupBy_select_grouping_composed_list_2(bool async)
+ {
+ await base.GroupBy_select_grouping_composed_list_2(async);
+
+ AssertSql(
+"""
+SELECT `c1`.`City`, `c0`.`CustomerID`, `c0`.`Address`, `c0`.`City`, `c0`.`CompanyName`, `c0`.`ContactName`, `c0`.`ContactTitle`, `c0`.`Country`, `c0`.`Fax`, `c0`.`Phone`, `c0`.`PostalCode`, `c0`.`Region`
+FROM (
+ SELECT `c`.`City`
+ FROM `Customers` AS `c`
+ GROUP BY `c`.`City`
+) AS `c1`
+LEFT JOIN `Customers` AS `c0` ON `c1`.`City` = `c0`.`City`
+ORDER BY `c1`.`City`, `c0`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_with_group_key_being_navigation(bool async)
+ {
+ await base.GroupBy_with_group_key_being_navigation(async);
+
+ AssertSql(
+"""
+SELECT `o0`.`OrderID`, `o0`.`CustomerID`, `o0`.`EmployeeID`, `o0`.`OrderDate`, COALESCE(SUM(`o`.`OrderID`), 0) AS `Aggregate`
+FROM `Order Details` AS `o`
+INNER JOIN `Orders` AS `o0` ON `o`.`OrderID` = `o0`.`OrderID`
+GROUP BY `o0`.`OrderID`, `o0`.`CustomerID`, `o0`.`EmployeeID`, `o0`.`OrderDate`
+""");
+ }
+
+ public override async Task GroupBy_with_group_key_being_nested_navigation(bool async)
+ {
+ await base.GroupBy_with_group_key_being_nested_navigation(async);
+
+ AssertSql(
+"""
+SELECT `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`, COALESCE(SUM(`o`.`OrderID`), 0) AS `Aggregate`
+FROM `Order Details` AS `o`
+INNER JOIN `Orders` AS `o0` ON `o`.`OrderID` = `o0`.`OrderID`
+LEFT JOIN `Customers` AS `c` ON `o0`.`CustomerID` = `c`.`CustomerID`
+GROUP BY `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`
+""");
+ }
+
+ public override async Task GroupBy_with_group_key_being_navigation_with_entity_key_projection(bool async)
+ {
+ await base.GroupBy_with_group_key_being_navigation_with_entity_key_projection(async);
+
+ AssertSql(
+"""
+SELECT `o0`.`OrderID`, `o0`.`CustomerID`, `o0`.`EmployeeID`, `o0`.`OrderDate`
+FROM `Order Details` AS `o`
+INNER JOIN `Orders` AS `o0` ON `o`.`OrderID` = `o0`.`OrderID`
+GROUP BY `o0`.`OrderID`, `o0`.`CustomerID`, `o0`.`EmployeeID`, `o0`.`OrderDate`
+""");
+ }
+
+ public override async Task GroupBy_with_group_key_being_navigation_with_complex_projection(bool async)
+ {
+ await base.GroupBy_with_group_key_being_navigation_with_complex_projection(async);
+
+ AssertSql();
+ }
+
+ public override async Task Count_after_GroupBy_aggregate(bool async)
+ {
+ await base.Count_after_GroupBy_aggregate(async);
+
+ AssertSql(
+"""
+SELECT COUNT(*)
+FROM (
+ SELECT 1
+ FROM `Orders` AS `o`
+ GROUP BY `o`.`CustomerID`
+) AS `o0`
+""");
+ }
+
+ public override async Task MinMax_after_GroupBy_aggregate(bool async)
+ {
+ await base.MinMax_after_GroupBy_aggregate(async);
+
+ AssertSql(
+"""
+SELECT MIN(`o0`.`c`)
+FROM (
+ SELECT COALESCE(SUM(`o`.`OrderID`), 0) AS `c`
+ FROM `Orders` AS `o`
+ GROUP BY `o`.`CustomerID`
+) AS `o0`
+""",
+ //
+ """
+SELECT MAX(`o0`.`c`)
+FROM (
+ SELECT COALESCE(SUM(`o`.`OrderID`), 0) AS `c`
+ FROM `Orders` AS `o`
+ GROUP BY `o`.`CustomerID`
+) AS `o0`
+""");
+ }
+
+ public override async Task All_after_GroupBy_aggregate(bool async)
+ {
+ await base.All_after_GroupBy_aggregate(async);
+
+ AssertSql(
+"""
+SELECT NOT EXISTS (
+ SELECT 1
+ FROM `Orders` AS `o`
+ GROUP BY `o`.`CustomerID`
+ HAVING FALSE)
+""");
+ }
+
+ public override async Task All_after_GroupBy_aggregate2(bool async)
+ {
+ await base.All_after_GroupBy_aggregate2(async);
+
+ AssertSql(
+"""
+SELECT NOT EXISTS (
+ SELECT 1
+ FROM `Orders` AS `o`
+ GROUP BY `o`.`CustomerID`
+ HAVING COALESCE(SUM(`o`.`OrderID`), 0) < 0)
+""");
+ }
+
+ public override async Task Any_after_GroupBy_aggregate(bool async)
+ {
+ await base.Any_after_GroupBy_aggregate(async);
+
+ AssertSql(
+"""
+SELECT EXISTS (
+ SELECT 1
+ FROM `Orders` AS `o`
+ GROUP BY `o`.`CustomerID`)
+""");
+ }
+
+ public override async Task Count_after_GroupBy_without_aggregate(bool async)
+ {
+ await base.Count_after_GroupBy_without_aggregate(async);
+
+ AssertSql(
+"""
+SELECT COUNT(*)
+FROM (
+ SELECT 1
+ FROM `Orders` AS `o`
+ GROUP BY `o`.`CustomerID`
+) AS `o0`
+""");
+ }
+
+ public override async Task Count_with_predicate_after_GroupBy_without_aggregate(bool async)
+ {
+ await base.Count_with_predicate_after_GroupBy_without_aggregate(async);
+
+ AssertSql(
+"""
+SELECT COUNT(*)
+FROM (
+ SELECT 1
+ FROM `Orders` AS `o`
+ GROUP BY `o`.`CustomerID`
+ HAVING COUNT(*) > 1
+) AS `o0`
+""");
+ }
+
+ public override async Task LongCount_after_GroupBy_without_aggregate(bool async)
+ {
+ await base.LongCount_after_GroupBy_without_aggregate(async);
+
+ AssertSql(
+"""
+SELECT COUNT(*)
+FROM (
+ SELECT 1
+ FROM `Orders` AS `o`
+ GROUP BY `o`.`CustomerID`
+) AS `o0`
+""");
+ }
+
+ public override async Task LongCount_with_predicate_after_GroupBy_without_aggregate(bool async)
+ {
+ await base.LongCount_with_predicate_after_GroupBy_without_aggregate(async);
+
+ AssertSql(
+"""
+SELECT COUNT(*)
+FROM (
+ SELECT 1
+ FROM `Orders` AS `o`
+ GROUP BY `o`.`CustomerID`
+ HAVING COUNT(*) > 1
+) AS `o0`
+""");
+ }
+
+ public override async Task Any_after_GroupBy_without_aggregate(bool async)
+ {
+ await base.Any_after_GroupBy_without_aggregate(async);
+
+ AssertSql(
+"""
+SELECT EXISTS (
+ SELECT 1
+ FROM `Orders` AS `o`
+ GROUP BY `o`.`CustomerID`)
+""");
+ }
+
+ public override async Task Any_with_predicate_after_GroupBy_without_aggregate(bool async)
+ {
+ await base.Any_with_predicate_after_GroupBy_without_aggregate(async);
+
+ AssertSql(
+"""
+SELECT EXISTS (
+ SELECT 1
+ FROM `Orders` AS `o`
+ GROUP BY `o`.`CustomerID`
+ HAVING COUNT(*) > 1)
+""");
+ }
+
+ public override async Task All_with_predicate_after_GroupBy_without_aggregate(bool async)
+ {
+ await base.All_with_predicate_after_GroupBy_without_aggregate(async);
+
+ AssertSql(
+"""
+SELECT NOT EXISTS (
+ SELECT 1
+ FROM `Orders` AS `o`
+ GROUP BY `o`.`CustomerID`
+ HAVING COUNT(*) <= 1)
+""");
+ }
+
+ public override async Task GroupBy_aggregate_followed_by_another_GroupBy_aggregate(bool async)
+ {
+ await base.GroupBy_aggregate_followed_by_another_GroupBy_aggregate(async);
+
+ AssertSql(
+"""
+SELECT `o1`.`Key0` AS `Key`, COALESCE(SUM(`o1`.`Count`), 0) AS `Count`
+FROM (
+ SELECT `o0`.`Count`, 1 AS `Key0`
+ FROM (
+ SELECT COUNT(*) AS `Count`
+ FROM `Orders` AS `o`
+ GROUP BY `o`.`CustomerID`
+ ) AS `o0`
+) AS `o1`
+GROUP BY `o1`.`Key0`
+""");
+ }
+
+ public override async Task GroupBy_nominal_type_count(bool async)
+ {
+ await base.GroupBy_nominal_type_count(async);
+
+ AssertSql(
+"""
+SELECT COUNT(*)
+FROM (
+ SELECT 1
+ FROM `Orders` AS `o`
+ GROUP BY `o`.`CustomerID`
+) AS `o0`
+""");
+ }
+
+ public override async Task Complex_query_with_groupBy_in_subquery4(bool async)
+ {
+ await base.Complex_query_with_groupBy_in_subquery4(async);
+
+ AssertSql(
+"""
+SELECT `c`.`CustomerID`, `s1`.`Sum`, `s1`.`Count`, `s1`.`Key`
+FROM `Customers` AS `c`
+LEFT JOIN LATERAL (
+ SELECT COALESCE(SUM(`s`.`OrderID`), 0) AS `Sum`, (
+ SELECT COUNT(*)
+ FROM (
+ SELECT `o0`.`CustomerID`, CONCAT(COALESCE(`c1`.`City`, ''), COALESCE(`o0`.`CustomerID`, '')) AS `Key`
+ FROM `Orders` AS `o0`
+ LEFT JOIN `Customers` AS `c1` ON `o0`.`CustomerID` = `c1`.`CustomerID`
+ WHERE `c`.`CustomerID` = `o0`.`CustomerID`
+ ) AS `s0`
+ LEFT JOIN `Customers` AS `c2` ON `s0`.`CustomerID` = `c2`.`CustomerID`
+ WHERE ((`s`.`Key` = `s0`.`Key`) OR (`s`.`Key` IS NULL AND (`s0`.`Key` IS NULL))) AND (CONCAT(COALESCE(`c2`.`City`, ''), COALESCE(`s0`.`CustomerID`, '')) LIKE 'Lon%')) AS `Count`, `s`.`Key`
+ FROM (
+ SELECT `o`.`OrderID`, CONCAT(COALESCE(`c0`.`City`, ''), COALESCE(`o`.`CustomerID`, '')) AS `Key`
+ FROM `Orders` AS `o`
+ LEFT JOIN `Customers` AS `c0` ON `o`.`CustomerID` = `c0`.`CustomerID`
+ WHERE `c`.`CustomerID` = `o`.`CustomerID`
+ ) AS `s`
+ GROUP BY `s`.`Key`
+) AS `s1` ON TRUE
+ORDER BY `c`.`CustomerID`
+""");
+ }
+
+ public override async Task Complex_query_with_group_by_in_subquery5(bool async)
+ {
+ await base.Complex_query_with_group_by_in_subquery5(async);
+
+ AssertSql(
+"""
+SELECT `s`.`c`, `s`.`ProductID`, `c1`.`CustomerID`, `c1`.`City`
+FROM (
+ SELECT COALESCE(SUM(`o`.`ProductID` + (`o`.`OrderID` * 1000)), 0) AS `c`, `o`.`ProductID`, MIN(`o`.`OrderID` / 100) AS `c0`
+ FROM `Order Details` AS `o`
+ INNER JOIN `Orders` AS `o0` ON `o`.`OrderID` = `o0`.`OrderID`
+ LEFT JOIN `Customers` AS `c` ON `o0`.`CustomerID` = `c`.`CustomerID`
+ WHERE `c`.`CustomerID` = 'ALFKI'
+ GROUP BY `o`.`ProductID`
+) AS `s`
+LEFT JOIN LATERAL (
+ SELECT `c0`.`CustomerID`, `c0`.`City`
+ FROM `Customers` AS `c0`
+ WHERE CHAR_LENGTH(`c0`.`CustomerID`) < `s`.`c0`
+) AS `c1` ON TRUE
+ORDER BY `s`.`ProductID`, `c1`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_scalar_subquery(bool async)
+ {
+ await base.GroupBy_scalar_subquery(async);
+
+ AssertSql(
+"""
+SELECT `o0`.`Key`, COUNT(*) AS `Count`
+FROM (
+ SELECT (
+ SELECT `c`.`ContactName`
+ FROM `Customers` AS `c`
+ WHERE `c`.`CustomerID` = `o`.`CustomerID`
+ LIMIT 1) AS `Key`
+ FROM `Orders` AS `o`
+) AS `o0`
+GROUP BY `o0`.`Key`
+""");
+ }
+
+ public override async Task GroupBy_scalar_aggregate_in_set_operation(bool async)
+ {
+ await base.GroupBy_scalar_aggregate_in_set_operation(async);
+
+ AssertSql(
+"""
+SELECT `c`.`CustomerID`, 0 AS `Sequence`
+FROM `Customers` AS `c`
+WHERE `c`.`CustomerID` LIKE 'F%'
+UNION
+SELECT `o`.`CustomerID`, 1 AS `Sequence`
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_aggregate_from_multiple_query_in_same_projection(bool async)
+ {
+ await base.GroupBy_aggregate_from_multiple_query_in_same_projection(async);
+
+ AssertSql();
+ }
+
+ public override async Task GroupBy_aggregate_from_multiple_query_in_same_projection_2(bool async)
+ {
+ await base.GroupBy_aggregate_from_multiple_query_in_same_projection_2(async);
+
+ AssertSql(
+"""
+SELECT `o`.`CustomerID` AS `Key`, COALESCE((
+ SELECT COUNT(*) + MIN(`o`.`OrderID`)
+ FROM `Employees` AS `e`
+ WHERE `e`.`City` = 'Seattle'
+ GROUP BY `e`.`City`
+ ORDER BY (SELECT 1)
+ LIMIT 1), 0) AS `A`
+FROM `Orders` AS `o`
+GROUP BY `o`.`CustomerID`
+""");
+ }
+
+ public override async Task GroupBy_aggregate_from_multiple_query_in_same_projection_3(bool async)
+ {
+ await base.GroupBy_aggregate_from_multiple_query_in_same_projection_3(async);
+
+ AssertSql();
+ }
+
+ public override async Task Select_uncorrelated_collection_with_groupby_when_outer_is_distinct(bool async)
+ {
+ await base.Select_uncorrelated_collection_with_groupby_when_outer_is_distinct(async);
+
+ AssertSql(
+"""
+SELECT `s`.`City`, `p1`.`ProductID`, `p2`.`c`, `p2`.`ProductID`
+FROM (
+ SELECT DISTINCT `c`.`City`
+ FROM `Orders` AS `o`
+ LEFT JOIN `Customers` AS `c` ON `o`.`CustomerID` = `c`.`CustomerID`
+ WHERE `o`.`CustomerID` LIKE 'A%'
+) AS `s`
+LEFT JOIN LATERAL (
+ SELECT `p`.`ProductID`
+ FROM `Products` AS `p`
+ GROUP BY `p`.`ProductID`
+) AS `p1` ON TRUE
+LEFT JOIN LATERAL (
+ SELECT COUNT(*) AS `c`, `p0`.`ProductID`
+ FROM `Products` AS `p0`
+ GROUP BY `p0`.`ProductID`
+) AS `p2` ON TRUE
+ORDER BY `s`.`City`, `p1`.`ProductID`
+""");
+ }
+
+ public override async Task Select_correlated_collection_after_GroupBy_aggregate_when_identifier_does_not_change(bool async)
+ {
+ await base.Select_correlated_collection_after_GroupBy_aggregate_when_identifier_does_not_change(async);
+
+ AssertSql(
+"""
+SELECT `c0`.`CustomerID`, `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+FROM (
+ SELECT `c`.`CustomerID`, `c`.`CustomerID` LIKE 'F%' AS `c`
+ FROM `Customers` AS `c`
+ GROUP BY `c`.`CustomerID`, `c`
+ HAVING `c`
+) AS `c0`
+LEFT JOIN `Orders` AS `o` ON `c0`.`CustomerID` = `o`.`CustomerID`
+ORDER BY `c0`.`CustomerID`
+""");
+ }
+
+ public override async Task Select_correlated_collection_after_GroupBy_aggregate_when_identifier_changes(bool async)
+ {
+ await base.Select_correlated_collection_after_GroupBy_aggregate_when_identifier_changes(async);
+
+ AssertSql(
+"""
+SELECT `o1`.`CustomerID`, `o0`.`OrderID`, `o0`.`CustomerID`, `o0`.`EmployeeID`, `o0`.`OrderDate`
+FROM (
+ SELECT `o`.`CustomerID`, (`o`.`CustomerID` LIKE 'F%') AND `o`.`CustomerID` IS NOT NULL AS `c`
+ FROM `Orders` AS `o`
+ GROUP BY `o`.`CustomerID`, `c`
+ HAVING `c`
+) AS `o1`
+LEFT JOIN `Orders` AS `o0` ON `o1`.`CustomerID` = `o0`.`CustomerID`
+ORDER BY `o1`.`CustomerID`
+""");
+ }
+
+ public override async Task Select_correlated_collection_after_GroupBy_aggregate_when_identifier_changes_to_complex(bool async)
+ {
+ await base.Select_correlated_collection_after_GroupBy_aggregate_when_identifier_changes_to_complex(async);
+
+ AssertSql(
+"""
+SELECT `o2`.`Key`, `o3`.`OrderID`, `o3`.`CustomerID`, `o3`.`EmployeeID`, `o3`.`OrderDate`
+FROM (
+ SELECT `o0`.`Key`, (`o0`.`Key` LIKE 'F%') AND `o0`.`Key` IS NOT NULL AS `c`
+ FROM (
+ SELECT CONCAT(COALESCE(`o`.`CustomerID`, ''), 'A') AS `Key`
+ FROM `Orders` AS `o`
+ ) AS `o0`
+ GROUP BY `o0`.`Key`, `c`
+ HAVING `c`
+) AS `o2`
+LEFT JOIN LATERAL (
+ SELECT `o1`.`OrderID`, `o1`.`CustomerID`, `o1`.`EmployeeID`, `o1`.`OrderDate`
+ FROM `Orders` AS `o1`
+ WHERE (CONCAT(COALESCE(`o1`.`CustomerID`, ''), 'A')) = `o2`.`Key`
+) AS `o3` ON TRUE
+ORDER BY `o2`.`Key`
+""");
+ }
+
+ [SupportedServerVersionCondition(nameof(ServerVersionSupport.OuterReferenceInMultiLevelSubquery))]
+ public override Task GroupBy_Count_in_projection(bool async)
+ {
+ return base.GroupBy_Count_in_projection(async);
+ }
+
+ [SupportedServerVersionCondition("8.0.22-mysql", "0.0.0-mariadb")]
+ public override Task GroupBy_group_Where_Select_Distinct_aggregate(bool async)
+ {
+ // See https://github.com/mysql-net/MySqlConnector/issues/898.
+ return base.GroupBy_group_Where_Select_Distinct_aggregate(async);
+ }
+
+ [SupportedServerVersionCondition("8.0.0-mysql", "0.0.0-mariadb")] // Is an issue issue in MySQL 5.7.34, but not in 8.0.25.
+ public override Task GroupBy_constant_with_where_on_grouping_with_aggregate_operators(bool async)
+ {
+ // See https://github.com/mysql-net/MySqlConnector/issues/980.
+ return base.GroupBy_constant_with_where_on_grouping_with_aggregate_operators(async);
+ }
+
+ [ConditionalFact]
+ public virtual void Check_all_tests_overridden()
+ => MySqlTestHelpers.AssertAllMethodsOverridden(GetType());
+
private void AssertSql(params string[] expected)
=> Fixture.TestSqlLoggerFactory.AssertBaseline(expected);
diff --git a/test/EFCore.MySql.FunctionalTests/Query/NorthwindIncludeQueryMySqlTest.cs b/test/EFCore.MySql.FunctionalTests/Query/NorthwindIncludeQueryMySqlTest.cs
index 10000fae2..404c0003e 100644
--- a/test/EFCore.MySql.FunctionalTests/Query/NorthwindIncludeQueryMySqlTest.cs
+++ b/test/EFCore.MySql.FunctionalTests/Query/NorthwindIncludeQueryMySqlTest.cs
@@ -22,9 +22,6 @@ public NorthwindIncludeQueryMySqlTest(
//Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper);
}
- protected override bool CanExecuteQueryString
- => true;
-
[ConditionalTheory]
public override Task Include_duplicate_collection(bool async)
{
diff --git a/test/EFCore.MySql.FunctionalTests/Query/NorthwindJoinQueryMySqlTest.cs b/test/EFCore.MySql.FunctionalTests/Query/NorthwindJoinQueryMySqlTest.cs
index 97b0f0c3e..4bced4161 100644
--- a/test/EFCore.MySql.FunctionalTests/Query/NorthwindJoinQueryMySqlTest.cs
+++ b/test/EFCore.MySql.FunctionalTests/Query/NorthwindJoinQueryMySqlTest.cs
@@ -17,9 +17,6 @@ public NorthwindJoinQueryMySqlTest(NorthwindQueryMySqlFixture true;
-
///
/// Needs explicit ordering of views to work consistently with MySQL and MariaDB.
/// But since CustomerViewModel is private, we can't even override the test case properly.
diff --git a/test/EFCore.MySql.FunctionalTests/Query/NorthwindKeylessEntitiesQueryMySqlTest.cs b/test/EFCore.MySql.FunctionalTests/Query/NorthwindKeylessEntitiesQueryMySqlTest.cs
index 5df5f6f22..dbb6af31c 100644
--- a/test/EFCore.MySql.FunctionalTests/Query/NorthwindKeylessEntitiesQueryMySqlTest.cs
+++ b/test/EFCore.MySql.FunctionalTests/Query/NorthwindKeylessEntitiesQueryMySqlTest.cs
@@ -16,9 +16,6 @@ public NorthwindKeylessEntitiesQueryMySqlTest(NorthwindQueryMySqlFixture true;
-
public override async Task KeylessEntity_with_nav_defining_query(bool async)
{
// FromSql mapping. Issue #21627.
diff --git a/test/EFCore.MySql.FunctionalTests/Query/NorthwindMiscellaneousQueryMySqlTest.cs b/test/EFCore.MySql.FunctionalTests/Query/NorthwindMiscellaneousQueryMySqlTest.cs
index 5039792ac..5fe746fd6 100644
--- a/test/EFCore.MySql.FunctionalTests/Query/NorthwindMiscellaneousQueryMySqlTest.cs
+++ b/test/EFCore.MySql.FunctionalTests/Query/NorthwindMiscellaneousQueryMySqlTest.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
@@ -30,87 +30,100 @@ public NorthwindMiscellaneousQueryMySqlTest(
//Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper);
}
- protected override bool CanExecuteQueryString
- => true;
-
public override async Task Select_bitwise_or(bool async)
{
await base.Select_bitwise_or(async);
- AssertSql(
- @"SELECT `c`.`CustomerID`, (`c`.`CustomerID` = 'ALFKI') | (`c`.`CustomerID` = 'ANATR') AS `Value`
+ AssertSql(
+"""
+SELECT `c`.`CustomerID`, `c`.`CustomerID` IN ('ALFKI', 'ANATR') AS `Value`
FROM `Customers` AS `c`
-ORDER BY `c`.`CustomerID`");
+ORDER BY `c`.`CustomerID`
+""");
}
public override async Task Select_bitwise_or_multiple(bool async)
{
await base.Select_bitwise_or_multiple(async);
- AssertSql(
- @"SELECT `c`.`CustomerID`, ((`c`.`CustomerID` = 'ALFKI') | (`c`.`CustomerID` = 'ANATR')) | (`c`.`CustomerID` = 'ANTON') AS `Value`
+ AssertSql(
+"""
+SELECT `c`.`CustomerID`, `c`.`CustomerID` IN ('ALFKI', 'ANATR', 'ANTON') AS `Value`
FROM `Customers` AS `c`
-ORDER BY `c`.`CustomerID`");
+ORDER BY `c`.`CustomerID`
+""");
}
public override async Task Select_bitwise_and(bool async)
{
await base.Select_bitwise_and(async);
- AssertSql(
- @"SELECT `c`.`CustomerID`, (`c`.`CustomerID` = 'ALFKI') & (`c`.`CustomerID` = 'ANATR') AS `Value`
+ AssertSql(
+"""
+SELECT `c`.`CustomerID`, FALSE AS `Value`
FROM `Customers` AS `c`
-ORDER BY `c`.`CustomerID`");
+ORDER BY `c`.`CustomerID`
+""");
}
public override async Task Select_bitwise_and_or(bool async)
{
await base.Select_bitwise_and_or(async);
- AssertSql(
- @"SELECT `c`.`CustomerID`, ((`c`.`CustomerID` = 'ALFKI') & (`c`.`CustomerID` = 'ANATR')) | (`c`.`CustomerID` = 'ANTON') AS `Value`
+ AssertSql(
+"""
+SELECT `c`.`CustomerID`, `c`.`CustomerID` = 'ANTON' AS `Value`
FROM `Customers` AS `c`
-ORDER BY `c`.`CustomerID`");
+ORDER BY `c`.`CustomerID`
+""");
}
public override async Task Where_bitwise_or_with_logical_or(bool async)
{
await base.Where_bitwise_or_with_logical_or(async);
- AssertSql(
- @"SELECT `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`
+ AssertSql(
+"""
+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` = 'ALFKI') | (`c`.`CustomerID` = 'ANATR')) OR (`c`.`CustomerID` = 'ANTON')");
+WHERE `c`.`CustomerID` IN ('ALFKI', 'ANATR', 'ANTON')
+""");
}
public override async Task Where_bitwise_and_with_logical_and(bool async)
{
await base.Where_bitwise_and_with_logical_and(async);
- AssertSql(
- @"SELECT `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`
+ AssertSql(
+"""
+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` = 'ALFKI') & (`c`.`CustomerID` = 'ANATR')) AND (`c`.`CustomerID` = 'ANTON')");
+WHERE FALSE
+""");
}
public override async Task Where_bitwise_or_with_logical_and(bool async)
{
await base.Where_bitwise_or_with_logical_and(async);
- AssertSql(
- @"SELECT `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`
+ AssertSql(
+"""
+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` = 'ALFKI') | (`c`.`CustomerID` = 'ANATR')) AND (`c`.`Country` = 'Germany')");
+WHERE `c`.`CustomerID` IN ('ALFKI', 'ANATR') AND (`c`.`Country` = 'Germany')
+""");
}
public override async Task Where_bitwise_and_with_logical_or(bool async)
{
await base.Where_bitwise_and_with_logical_or(async);
- AssertSql(
- @"SELECT `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`
+ AssertSql(
+"""
+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` = 'ALFKI') & (`c`.`CustomerID` = 'ANATR')) OR (`c`.`CustomerID` = 'ANTON')");
+WHERE `c`.`CustomerID` = 'ANTON'
+""");
}
public override async Task Where_bitwise_binary_not(bool async)
@@ -130,9 +143,11 @@ public override async Task Where_bitwise_binary_and(bool async)
await base.Where_bitwise_binary_and(async);
AssertSql(
- @"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+"""
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`OrderID` & 10248) = 10248");
+WHERE CAST(`o`.`OrderID` & 10248 AS signed) = 10248
+""");
}
public override async Task Where_bitwise_binary_or(bool async)
@@ -140,29 +155,35 @@ public override async Task Where_bitwise_binary_or(bool async)
await base.Where_bitwise_binary_or(async);
AssertSql(
- @"SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+"""
+SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
FROM `Orders` AS `o`
-WHERE (`o`.`OrderID` | 10248) = 10248");
+WHERE CAST(`o`.`OrderID` | 10248 AS signed) = 10248
+""");
}
public override async Task Select_bitwise_or_with_logical_or(bool async)
{
await base.Select_bitwise_or_with_logical_or(async);
- AssertSql(
- @"SELECT `c`.`CustomerID`, ((`c`.`CustomerID` = 'ALFKI') | (`c`.`CustomerID` = 'ANATR')) OR (`c`.`CustomerID` = 'ANTON') AS `Value`
+ AssertSql(
+"""
+SELECT `c`.`CustomerID`, `c`.`CustomerID` IN ('ALFKI', 'ANATR', 'ANTON') AS `Value`
FROM `Customers` AS `c`
-ORDER BY `c`.`CustomerID`");
+ORDER BY `c`.`CustomerID`
+""");
}
public override async Task Select_bitwise_and_with_logical_and(bool async)
{
await base.Select_bitwise_and_with_logical_and(async);
- AssertSql(
- @"SELECT `c`.`CustomerID`, ((`c`.`CustomerID` = 'ALFKI') & (`c`.`CustomerID` = 'ANATR')) AND (`c`.`CustomerID` = 'ANTON') AS `Value`
+ AssertSql(
+"""
+SELECT `c`.`CustomerID`, FALSE AS `Value`
FROM `Customers` AS `c`
-ORDER BY `c`.`CustomerID`");
+ORDER BY `c`.`CustomerID`
+""");
}
[ConditionalTheory]
diff --git a/test/EFCore.MySql.FunctionalTests/Query/NorthwindQueryMySqlFixture.cs b/test/EFCore.MySql.FunctionalTests/Query/NorthwindQueryMySqlFixture.cs
index 3cf738ece..f28fe556e 100644
--- a/test/EFCore.MySql.FunctionalTests/Query/NorthwindQueryMySqlFixture.cs
+++ b/test/EFCore.MySql.FunctionalTests/Query/NorthwindQueryMySqlFixture.cs
@@ -10,7 +10,7 @@
namespace Pomelo.EntityFrameworkCore.MySql.FunctionalTests.Query
{
public class NorthwindQueryMySqlFixture : NorthwindQueryRelationalFixture
- where TModelCustomizer : IModelCustomizer, new()
+ where TModelCustomizer : ITestModelCustomizer, new()
{
protected override ITestStoreFactory TestStoreFactory => MySqlNorthwindTestStoreFactory.Instance;
protected override Type ContextType => typeof(NorthwindMySqlContext);
diff --git a/test/EFCore.MySql.FunctionalTests/Query/NorthwindSelectQueryMySqlTest.MySql.cs b/test/EFCore.MySql.FunctionalTests/Query/NorthwindSelectQueryMySqlTest.MySql.cs
index 3b12c1b10..7b97a7a07 100644
--- a/test/EFCore.MySql.FunctionalTests/Query/NorthwindSelectQueryMySqlTest.MySql.cs
+++ b/test/EFCore.MySql.FunctionalTests/Query/NorthwindSelectQueryMySqlTest.MySql.cs
@@ -33,28 +33,26 @@ await AssertQuery(
ss => ss.Set()
.Where(o => o.CustomerID == "ALFKI" &&
o.OrderDate != null)
- .GroupBy(o => new {o.CustomerID, o.OrderDate.Value.Year})
- .Select(g => new {g.Key.Year, Count = g.Count()})
+ .GroupBy(o => new {o.CustomerID, o.OrderDate.Value.Date})
+ .Select(g => new {g.Key.Date.Year, Count = g.Count()})
.Where(k => k.Year == 1995)
.OrderBy(k => k.Year),
assertOrder: true,
assertEmpty: true); // TODO: Use a linq query that does not return an empty result.
- AssertSql(
-"""
-SELECT `o3`.`Year`, `o3`.`Count`
+ AssertSql(
+ @"SELECT `o1`.`Year`, `o1`.`Count`
FROM (
- SELECT `o0`.`Year`, COUNT(*) AS `Count`, `o0`.`CustomerID`, `o0`.`Year` = 1995 AS `c`
+ SELECT EXTRACT(year FROM `o0`.`Date`) AS `Year`, COUNT(*) AS `Count`, (EXTRACT(year FROM `o0`.`Date`) = 1995) AND EXTRACT(year FROM `o0`.`Date`) IS NOT NULL AS `c`
FROM (
- SELECT `o`.`CustomerID`, EXTRACT(year FROM `o`.`OrderDate`) AS `Year`
+ SELECT `o`.`CustomerID`, CONVERT(`o`.`OrderDate`, date) AS `Date`
FROM `Orders` AS `o`
WHERE (`o`.`CustomerID` = 'ALFKI') AND `o`.`OrderDate` IS NOT NULL
) AS `o0`
- GROUP BY `o0`.`CustomerID`, `o0`.`Year`, `c`
+ GROUP BY `o0`.`CustomerID`, `o0`.`Date`, `c`
HAVING `c`
-) AS `o3`
-ORDER BY `o3`.`Year`
-""");
+) AS `o1`
+ORDER BY `o1`.`Year`");
}
[ConditionalTheory]
@@ -80,9 +78,9 @@ await AssertQueryScalar(
AssertSql(
"""
-SELECT `o7`.`Year`
+SELECT `o3`.`Year`
FROM (
- SELECT `o0`.`Year`, `o0`.`CustomerID`, `o0`.`Year` = 1995 AS `c`
+ SELECT `o0`.`Year`, (`o0`.`Year` = 1995) AND `o0`.`Year` IS NOT NULL AS `c`
FROM (
SELECT `o`.`CustomerID`, EXTRACT(year FROM `o`.`OrderDate`) AS `Year`
FROM `Orders` AS `o`
@@ -90,7 +88,7 @@ await AssertQueryScalar(
) AS `o0`
GROUP BY `o0`.`CustomerID`, `o0`.`Year`, `c`
HAVING `c`
-) AS `o7`
+) AS `o3`
UNION ALL
SELECT `o2`.`Key` AS `Year`
FROM (
diff --git a/test/EFCore.MySql.FunctionalTests/Query/NorthwindSelectQueryMySqlTest.cs b/test/EFCore.MySql.FunctionalTests/Query/NorthwindSelectQueryMySqlTest.cs
index ec6db87b8..e7c067f2e 100644
--- a/test/EFCore.MySql.FunctionalTests/Query/NorthwindSelectQueryMySqlTest.cs
+++ b/test/EFCore.MySql.FunctionalTests/Query/NorthwindSelectQueryMySqlTest.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
@@ -26,9 +26,6 @@ public NorthwindSelectQueryMySqlTest(
//Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper);
}
- protected override bool CanExecuteQueryString
- => true;
-
[ConditionalTheory]
public override async Task Select_datetime_year_component(bool async)
{
diff --git a/test/EFCore.MySql.FunctionalTests/Query/NorthwindSetOperationsQueryMySqlTest.cs b/test/EFCore.MySql.FunctionalTests/Query/NorthwindSetOperationsQueryMySqlTest.cs
index 50ea0c948..610bd82b5 100644
--- a/test/EFCore.MySql.FunctionalTests/Query/NorthwindSetOperationsQueryMySqlTest.cs
+++ b/test/EFCore.MySql.FunctionalTests/Query/NorthwindSetOperationsQueryMySqlTest.cs
@@ -22,9 +22,6 @@ public NorthwindSetOperationsQueryMySqlTest(
//Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper);
}
- protected override bool CanExecuteQueryString
- => true;
-
public override async Task Client_eval_Union_FirstOrDefault(bool async)
{
// Client evaluation in projection. Issue #16243.
diff --git a/test/EFCore.MySql.FunctionalTests/Query/NorthwindSqlQueryMySqlTest.cs b/test/EFCore.MySql.FunctionalTests/Query/NorthwindSqlQueryMySqlTest.cs
index 0166febd2..63141002e 100644
--- a/test/EFCore.MySql.FunctionalTests/Query/NorthwindSqlQueryMySqlTest.cs
+++ b/test/EFCore.MySql.FunctionalTests/Query/NorthwindSqlQueryMySqlTest.cs
@@ -3,6 +3,7 @@
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.TestUtilities;
using MySqlConnector;
+using Pomelo.EntityFrameworkCore.MySql.FunctionalTests.TestUtilities;
using Xunit;
using Xunit.Abstractions;
@@ -18,7 +19,7 @@ public NorthwindSqlQueryMySqlTest(NorthwindQueryMySqlFixture TestHelpers.AssertAllMethodsOverridden(GetType());
+ => MySqlTestHelpers.AssertAllMethodsOverridden(GetType());
public override async Task SqlQueryRaw_over_int(bool async)
{
diff --git a/test/EFCore.MySql.FunctionalTests/Query/NorthwindStringComparisonFunctionsQueryMySqlTest.cs b/test/EFCore.MySql.FunctionalTests/Query/NorthwindStringComparisonFunctionsQueryMySqlTest.cs
new file mode 100644
index 000000000..ad16ef30d
--- /dev/null
+++ b/test/EFCore.MySql.FunctionalTests/Query/NorthwindStringComparisonFunctionsQueryMySqlTest.cs
@@ -0,0 +1,769 @@
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+using JetBrains.Annotations;
+using Microsoft.EntityFrameworkCore.Query;
+using Microsoft.EntityFrameworkCore.TestModels.Northwind;
+using Microsoft.EntityFrameworkCore.TestUtilities;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace Pomelo.EntityFrameworkCore.MySql.FunctionalTests.Query
+{
+ public class NorthwindStringComparisonFunctionsQueryMySqlTest : QueryTestBase>
+ {
+ public NorthwindStringComparisonFunctionsQueryMySqlTest(
+ [NotNull] CaseSensitiveWithStringComparisonNorthwindQueryMySqlFixture fixture,
+ ITestOutputHelper testOutputHelper)
+ : base(fixture)
+ {
+ ClearLog();
+ //Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper);
+ }
+
+ [ConditionalTheory]
+ [InlineData(StringComparison.OrdinalIgnoreCase, 1, false)]
+ [InlineData(StringComparison.OrdinalIgnoreCase, 1, true)]
+ [InlineData(StringComparison.CurrentCultureIgnoreCase, 1, false)]
+ [InlineData(StringComparison.CurrentCultureIgnoreCase, 1, true)]
+ [InlineData(StringComparison.InvariantCultureIgnoreCase, 1, false)]
+ [InlineData(StringComparison.InvariantCultureIgnoreCase, 1, true)]
+ [InlineData(StringComparison.Ordinal, 0, false)]
+ [InlineData(StringComparison.Ordinal, 0, true)]
+ [InlineData(StringComparison.CurrentCulture, 0, false)]
+ [InlineData(StringComparison.CurrentCulture, 0, true)]
+ [InlineData(StringComparison.InvariantCulture, 0, false)]
+ [InlineData(StringComparison.InvariantCulture, 0, true)]
+ public async Task StringEquals_with_comparison_parameter(StringComparison comparison, int expected, bool async)
+ {
+ await AssertQuery(
+ async,
+ ss => ss.Set().Where(c => c.CustomerID.Equals("anton", comparison)),
+ assertEmpty: expected == 0);
+
+ // When the comparison parameter is not a constant, we have to use a case
+ // statement
+ AssertSql(
+ $@"@__comparison_0='{comparison:D}'
+
+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 CASE
+ WHEN @__comparison_0 IN (4, 0, 2) THEN `c`.`CustomerID` = CONVERT('anton' USING utf8mb4) COLLATE utf8mb4_bin
+ ELSE LCASE(`c`.`CustomerID`) = CONVERT(LCASE('anton') USING utf8mb4) COLLATE utf8mb4_bin
+END");
+ }
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public async Task StringEquals_ordinal(bool async)
+ {
+ await AssertQuery(
+ async,
+ ss => ss.Set().Where(c => c.CustomerID.Equals("anton", StringComparison.Ordinal)),
+ assertEmpty: true);
+
+ AssertSql(@"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` = CONVERT('anton' USING utf8mb4) COLLATE utf8mb4_bin");
+ }
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public async Task StringEquals_invariant(bool async)
+ {
+ await AssertQuery(
+ async,
+ ss => ss.Set().Where(c => c.CustomerID.Equals("anton", StringComparison.CurrentCulture)),
+ assertEmpty: true);
+
+ AssertSql(@"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` = CONVERT('anton' USING utf8mb4) COLLATE utf8mb4_bin");
+ }
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public async Task StringEquals_current(bool async)
+ {
+ await AssertQuery(
+ async,
+ ss => ss.Set().Where(c => c.CustomerID.Equals("anton", StringComparison.InvariantCulture)),
+ assertEmpty: true);
+
+ AssertSql(@"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` = CONVERT('anton' USING utf8mb4) COLLATE utf8mb4_bin");
+ }
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public async Task StringEquals_ordinal_ignore_case(bool async)
+ {
+ await AssertQuery(
+ async,
+ ss => ss.Set().Where(c => c.CustomerID.Equals("anton", StringComparison.OrdinalIgnoreCase)));
+
+ AssertSql(
+ @"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 LCASE(`c`.`CustomerID`) = CONVERT(LCASE('anton') USING utf8mb4) COLLATE utf8mb4_bin");
+ }
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public async Task StringEquals_current_ignore_case(bool async)
+ {
+ await AssertQuery(
+ async,
+ ss => ss.Set