From 0e6746644e075d888ae1856e712b389df9bafd7e Mon Sep 17 00:00:00 2001 From: Tim Thompson Date: Mon, 6 Jun 2022 00:52:31 +1000 Subject: [PATCH] =?UTF-8?q?fix=20#167:=20Ensure=20that=20a=20sql=20server?= =?UTF-8?q?=20database=20name=20that=20differs=20only=20by=E2=80=A6=20(#19?= =?UTF-8?q?5)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- grate.unittests/Generic/GenericDatabase.cs | 18 ++++++++++++++ grate/Migration/SqlServerDatabase.cs | 28 ++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/grate.unittests/Generic/GenericDatabase.cs b/grate.unittests/Generic/GenericDatabase.cs index 95cc7a70..1ab33690 100644 --- a/grate.unittests/Generic/GenericDatabase.cs +++ b/grate.unittests/Generic/GenericDatabase.cs @@ -87,6 +87,24 @@ public async Task Does_not_need_admin_connection_if_database_already_exists() Assert.DoesNotThrowAsync(() => migrator.Migrate()); } + [Test] + public async Task Does_not_needlessly_apply_case_sensitive_database_name_checks_Issue_167() + { + // There's a bug where if the database name specified by the user differs from the actual database only by case then + // Grate currently attempts to create the database again, only for it to fail on the DBMS (Sql Server bug only). + + var db = "CASEDATABASE"; + await CreateDatabase(db); + + // Check that the database has been created + IEnumerable databasesBeforeMigration = await GetDatabases(); + databasesBeforeMigration.Should().Contain(db); + + 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()); + } + protected virtual async Task CreateDatabase(string db) { using (new TransactionScope(TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled)) diff --git a/grate/Migration/SqlServerDatabase.cs b/grate/Migration/SqlServerDatabase.cs index b70c7c1e..44a534e5 100644 --- a/grate/Migration/SqlServerDatabase.cs +++ b/grate/Migration/SqlServerDatabase.cs @@ -1,12 +1,16 @@ using System; using System.Data.Common; +using System.Linq; using System.Threading.Tasks; using System.Transactions; +using Dapper; using grate.Configuration; using grate.Infrastructure; using Microsoft.Data.SqlClient; using Microsoft.Extensions.Logging; +using static System.Data.CommandType; + namespace grate.Migration; public class SqlServerDatabase : AnsiSqlDatabase @@ -68,4 +72,28 @@ WITH NOUNLOAD Logger.LogInformation("Database {DbName} successfully restored from path {Path}.", DatabaseName, backupPath); } + + public override async Task DatabaseExists() + { + // For Bug #167. Sql Server is causing issues when the database name passed in differs only in case from one already existing on the server. + // There's currently no point adding to ISyntax for this as all the other DBMS's would just be a NOP. + + // This should also mean that a SQL server running a Case Sensitive collation _also_ works as expected + var sql = $"select name from sys.databases where [name] = '{DatabaseName}'"; + try + { + await OpenConnection(); + var results = await Connection.QueryAsync(sql, 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(); + } + } }