Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

215 - MariaDB case insensitive db name support #216

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 42 additions & 9 deletions grate.unittests/Generic/GenericDatabase.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using System.Threading.Tasks;
Expand Down Expand Up @@ -26,7 +27,7 @@ public async Task Is_created_if_confed_and_it_does_not_exist()
await migrator.Migrate();

IEnumerable<string> databases = await GetDatabases();
databases.Should().Contain(db);
Exist(databases, db).Should().BeTrue();
}

[Test]
Expand All @@ -35,7 +36,7 @@ public async Task Is_not_created_if_not_confed()
var db = "SOMEOTHERDATABASE";

IEnumerable<string> databasesBeforeMigration = await GetDatabases();
databasesBeforeMigration.Should().NotContain(db);
Exist(databasesBeforeMigration, db).Should().BeFalse();

await using var migrator = GetMigrator(GetConfiguration(db, false));

Expand All @@ -47,7 +48,7 @@ public async Task Is_not_created_if_not_confed()

// Ensure that the database was in fact not created
IEnumerable<string> databases = await GetDatabases();
databases.Should().NotContain(db);
Exist(databases, db).Should().BeFalse();
}

[Test]
Expand All @@ -60,14 +61,14 @@ public async Task Does_not_error_if_confed_to_create_but_already_exists()

// Check that the database has been created
IEnumerable<string> databasesBeforeMigration = await GetDatabases();
databasesBeforeMigration.Should().Contain(db);
Exist(databasesBeforeMigration, db).Should().BeTrue();

await using var migrator = GetMigrator(GetConfiguration(db, true));

// There should be no errors running the migration
Assert.DoesNotThrowAsync(() => migrator.Migrate());
}

[Test]
public async Task Does_not_need_admin_connection_if_database_already_exists()
{
Expand All @@ -78,7 +79,7 @@ public async Task Does_not_need_admin_connection_if_database_already_exists()

// Check that the database has been created
IEnumerable<string> databasesBeforeMigration = await GetDatabases();
databasesBeforeMigration.Should().Contain(db);
Exist(databasesBeforeMigration, db).Should().BeTrue();

// Change the admin connection string to rubbish and run the migration
await using var migrator = GetMigrator(GetConfiguration(db, true, "Invalid stuff"));
Expand All @@ -98,13 +99,36 @@ public async Task Does_not_needlessly_apply_case_sensitive_database_name_checks_

// Check that the database has been created
IEnumerable<string> databasesBeforeMigration = await GetDatabases();
databasesBeforeMigration.Should().Contain(db);
Exist(databasesBeforeMigration, db).Should().BeTrue();

await using var migrator = GetMigrator(GetConfiguration(db.ToLower(), true)); // ToLower is important here, this reproduces the bug in #167
// There should be no errors running the migration
Assert.DoesNotThrowAsync(() => migrator.Migrate());
}

[Test]
public async Task Should_create_the_databases_with_name_as_case_is_supported()
{
const string db = "CaseSensitiveName";
var lowercaseDbName = db.ToLower();

await CreateDatabase(db);
await CreateDatabase(lowercaseDbName);

var databases = (await GetDatabases()).ToArray();

if (Context.DatabaseNamingCaseSensitive)
{
databases.Should().Contain(db);
databases.Should().Contain(lowercaseDbName);
}
else
{
var matches = databases.Count(x => string.Equals(x, db, StringComparison.OrdinalIgnoreCase));
matches.Should().Be(1);
}
}

protected virtual async Task CreateDatabase(string db)
{
using (new TransactionScope(TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled))
Expand All @@ -127,7 +151,7 @@ protected virtual async Task CreateDatabase(string db)

protected virtual async Task<IEnumerable<string>> GetDatabases()
{
IEnumerable<string> databases =Enumerable.Empty<string>();
IEnumerable<string> databases = Enumerable.Empty<string>();
string sql = Context.Syntax.ListDatabases;

using (new TransactionScope(TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled))
Expand All @@ -146,6 +170,15 @@ protected virtual async Task<IEnumerable<string>> GetDatabases()
return databases;
}

private bool Exist(IEnumerable<string> databases, string db)
{
var comparer = Context.DatabaseNamingCaseSensitive
? StringComparer.InvariantCulture
: StringComparer.InvariantCultureIgnoreCase;

return databases.Contains(db, comparer);
}

protected virtual bool ThrowOnMissingDatabase => true;


Expand Down
23 changes: 16 additions & 7 deletions grate.unittests/MariaDB/Database.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
using grate.unittests.TestInfrastructure;
using grate.unittests.TestInfrastructure;
using NUnit.Framework;

namespace grate.unittests.MariaDB;

[TestFixture]
[Category("MariaDB")]
public class Database: Generic.GenericDatabase
public class Database
{
protected override IGrateTestContext Context => GrateTestContext.MariaDB;

}
[TestFixture]
[Category("MariaDB")]
public class Default : Generic.GenericDatabase
{
protected override IGrateTestContext Context => GrateTestContext.MariaDB;
}

[TestFixture]
[Category("MariaDBCaseInsensitive")]
public class CaseInsensitive : Generic.GenericDatabase
{
protected override IGrateTestContext Context => GrateTestContext.MariaDBCaseInsensitive;
}
}
23 changes: 17 additions & 6 deletions grate.unittests/MariaDB/SetupTestEnvironment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,21 @@

namespace grate.unittests.MariaDB;

[SetUpFixture]
[Category("MariaDB")]
public class SetupTestEnvironment : Generic.SetupDockerTestEnvironment
public class SetupTestEnvironment
{
protected override IGrateTestContext GrateTestContext => unittests.GrateTestContext.MariaDB;
protected override IDockerTestContext DockerTestContext => unittests.GrateTestContext.MariaDB;
}
[SetUpFixture]
[Category("MariaDB")]
public class Default : Generic.SetupDockerTestEnvironment
{
protected override IGrateTestContext GrateTestContext => unittests.GrateTestContext.MariaDB;
protected override IDockerTestContext DockerTestContext => unittests.GrateTestContext.MariaDB;
}

[SetUpFixture]
[Category("MariaDBCaseInsensitive")]
public class CaseInsensitive : Generic.SetupDockerTestEnvironment
{
protected override IGrateTestContext GrateTestContext => unittests.GrateTestContext.MariaDBCaseInsensitive;
protected override IDockerTestContext DockerTestContext => unittests.GrateTestContext.MariaDBCaseInsensitive;
}
}
21 changes: 13 additions & 8 deletions grate.unittests/SqLite/Database.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Collections.Generic;
using System.Data.Common;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
Expand All @@ -16,12 +17,16 @@ public class Database: Generic.GenericDatabase

protected override async Task CreateDatabase(string db)
{
await using var conn = new SqliteConnection(Context.ConnectionString(db));
conn.Open();
await using var cmd = conn.CreateCommand();
var sql = "CREATE TABLE dummy(name VARCHAR(1))";
cmd.CommandText = sql;
await cmd.ExecuteNonQueryAsync();
try
{
await using var conn = new SqliteConnection(Context.ConnectionString(db));
conn.Open();
await using var cmd = conn.CreateCommand();
var sql = "CREATE TABLE dummy(name VARCHAR(1))";
cmd.CommandText = sql;
await cmd.ExecuteNonQueryAsync();
}
catch (DbException) { }
}

protected override async Task<IEnumerable<string>> GetDatabases()
Expand All @@ -36,4 +41,4 @@ protected override async Task<IEnumerable<string>> GetDatabases()
}

protected override bool ThrowOnMissingDatabase => false;
}
}
4 changes: 3 additions & 1 deletion grate.unittests/TestContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ public static class GrateTestContext
internal static readonly OracleGrateTestContext Oracle = new();
internal static readonly PostgreSqlGrateTestContext PostgreSql = new();
// ReSharper disable once InconsistentNaming
internal static readonly MariaDbGrateTestContext MariaDB = new();
internal static readonly MariaDbGrateTestContext MariaDB = new(true);
// ReSharper disable once InconsistentNaming
internal static readonly MariaDbGrateTestContext MariaDBCaseInsensitive = new(false);
internal static readonly SqliteGrateTestContext Sqlite = new();
}
2 changes: 2 additions & 0 deletions grate.unittests/TestInfrastructure/IGrateTestContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,6 @@ public GrateMigrator GetMigrator(string databaseName, KnownFolders knownFolders,
}

bool SupportsCreateDatabase { get; }

bool DatabaseNamingCaseSensitive { get; }
}
11 changes: 9 additions & 2 deletions grate.unittests/TestInfrastructure/MariaDbGrateTestContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,18 @@ namespace grate.unittests.TestInfrastructure;

class MariaDbGrateTestContext : TestContextBase, IGrateTestContext, IDockerTestContext
{
public MariaDbGrateTestContext(bool databaseNamingCaseSensitive)
{
DatabaseNamingCaseSensitive = databaseNamingCaseSensitive;
}

public string AdminPassword { get; set; } = default!;
public int? Port { get; set; }

public string DockerCommand(string serverName, string adminPassword) =>
$"run -d --name {serverName} -e MYSQL_ROOT_PASSWORD={adminPassword} -P mariadb:10.5.9";
DatabaseNamingCaseSensitive
? $"run -d --name {serverName} -e MYSQL_ROOT_PASSWORD={adminPassword} -P mariadb:10.5.9"
: $"run -d --name {serverName} -e MYSQL_ROOT_PASSWORD={adminPassword} -P mariadb:10.5.9 --lower_case_table_names=1";

public string AdminConnectionString => $"Server=localhost;Port={Port};Database=mysql;Uid=root;Pwd={AdminPassword}";
public string ConnectionString(string database) => $"Server=localhost;Port={Port};Database={database};Uid=root;Pwd={AdminPassword}";
Expand All @@ -37,7 +44,7 @@ public string DockerCommand(string serverName, string adminPassword) =>
SleepTwoSeconds = "SELECT SLEEP(2);"
};


public string ExpectedVersionPrefix => "10.5.9-MariaDB";
public bool SupportsCreateDatabase => true;
public bool DatabaseNamingCaseSensitive { get; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,5 @@ public string DockerCommand(string serverName, string adminPassword) =>

public string ExpectedVersionPrefix => "Oracle Database 11g Express Edition Release 11.2.0.2.0 - 64bit Production";
public bool SupportsCreateDatabase => true;
public bool DatabaseNamingCaseSensitive => false;
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,5 @@ public string DockerCommand(string serverName, string adminPassword) =>

public string ExpectedVersionPrefix => "PostgreSQL 14.";
public bool SupportsCreateDatabase => true;
public bool DatabaseNamingCaseSensitive => true;
}
3 changes: 2 additions & 1 deletion grate.unittests/TestInfrastructure/SqLiteGrateTestContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,5 @@ class SqliteGrateTestContext : TestContextBase, IGrateTestContext

public string ExpectedVersionPrefix => "3.32.3";
public bool SupportsCreateDatabase => false;
}
public bool DatabaseNamingCaseSensitive => false;
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,5 @@ public string DockerCommand(string serverName, string adminPassword) =>

public string ExpectedVersionPrefix => "Microsoft SQL Server 2019";
public bool SupportsCreateDatabase => true;
public bool DatabaseNamingCaseSensitive => false;
}
27 changes: 25 additions & 2 deletions grate/Migration/MariaDbDatabase.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
using System.Data.Common;
using System.Data;
using System.Data.Common;
using System.Linq;
using System.Threading.Tasks;
using Dapper;
using grate.Infrastructure;
using Microsoft.Extensions.Logging;
using MySqlConnector;
Expand All @@ -20,4 +23,24 @@ public override Task RestoreDatabase(string backupPath)
{
throw new System.NotImplementedException("Restoring a database from file is not currently supported for Maria DB.");
}
}

public override async Task<bool> DatabaseExists()
{
var sql = $"SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = '{DatabaseName}'";
try
{
await OpenConnection();
var results = await Connection.QueryAsync<string>(sql, commandType: CommandType.Text);
return results.Any();
}
catch (DbException ex)
{
Logger.LogDebug(ex, "An unexpected error occurred performing the CheckDatabaseExists check: {ErrorMessage}", ex.Message);
return false; // base method also returns false on any DbException
}
finally
{
await CloseConnection();
}
}
}