diff --git a/grate.unittests/Generic/Running_MigrationScripts/Versioning_The_Database.cs b/grate.unittests/Generic/Running_MigrationScripts/Versioning_The_Database.cs index 7a880db2..f833d985 100644 --- a/grate.unittests/Generic/Running_MigrationScripts/Versioning_The_Database.cs +++ b/grate.unittests/Generic/Running_MigrationScripts/Versioning_The_Database.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Dapper; @@ -22,7 +22,7 @@ public async Task Returns_The_New_Version_Id() var db = TestConfig.RandomDatabase(); GrateMigrator? migrator; - + var parent = CreateRandomTempDirectory(); var knownFolders = FoldersConfiguration.Default(null); CreateDummySql(parent, knownFolders[Sprocs]); @@ -49,7 +49,7 @@ public async Task Does_Not_Create_Versions_When_Dryrun() { //for bug #204 - when running --baseline and --dryrun on a new db it shouldn't create the grate schema's etc var db = TestConfig.RandomDatabase(); - + var parent = CreateRandomTempDirectory(); var knownFolders = FoldersConfiguration.Default(null); @@ -80,7 +80,7 @@ public async Task Creates_A_New_Version_In_Progress() CreateDummySql(parent, knownFolders[Up]); long newVersionId = 0; - + await using (migrator = Context.GetMigrator(db, parent, knownFolders)) { //Calling migrate here to setup the database and such. @@ -100,4 +100,5 @@ public async Task Creates_A_New_Version_In_Progress() var version = entries.Single(x => x.version == dbVersion); version.status.Should().Be(MigrationStatus.InProgress); } + } diff --git a/grate.unittests/SqlServer/Running_MigrationScripts/Versioning_The_Database.cs b/grate.unittests/SqlServer/Running_MigrationScripts/Versioning_The_Database.cs index 6d4094b2..0b7797df 100644 --- a/grate.unittests/SqlServer/Running_MigrationScripts/Versioning_The_Database.cs +++ b/grate.unittests/SqlServer/Running_MigrationScripts/Versioning_The_Database.cs @@ -1,12 +1,47 @@ +using System.Threading.Tasks; +using grate.Configuration; using grate.unittests.TestInfrastructure; using NUnit.Framework; +using static grate.Configuration.KnownFolderKeys; + namespace grate.unittests.SqlServer.Running_MigrationScripts; [TestFixture] [Category("SqlServer")] +[NonParallelizable] // ReSharper disable once InconsistentNaming public class Versioning_The_Database: Generic.Running_MigrationScripts.Versioning_The_Database { protected override IGrateTestContext Context => GrateTestContext.SqlServer; + + + [Test] + public async Task Bug230_Uses_Server_Casing_Rules_For_Schema() + { + //for bug #230 - when targeting an existing schema use the servers casing rules, not .Net's + var db = TestConfig.RandomDatabase(); + var parent = CreateRandomTempDirectory(); + var knownFolders = FoldersConfiguration.Default(null); + + CreateDummySql(parent, knownFolders[Sprocs]); // make sure there's something that could be logged... + + await using (var migrator = Context.GetMigrator(db, parent, knownFolders)) + { + await migrator.Migrate(); + Assert.True(await migrator.DbMigrator.Database.VersionTableExists()); // we migrated into the `grate` schema. + } + + // Now we'll run again with the same name but different cased schema + var grateConfig = Context.GetConfiguration(db, parent, knownFolders) with + { + SchemaName = "GRATE" + }; + + await using (var migrator = Context.GetMigrator(grateConfig)) + { + await migrator.Migrate(); // should either reuse the existing schema if a case-insensitive server, or create a new second schema for use if case-sensitive. + Assert.True(await migrator.DbMigrator.Database.VersionTableExists()); // we migrated into the `GRATE` schema, which may be the same as 'grate' depending on server settings. + } + } } diff --git a/grate.unittests/SqlServerCaseSensitive/Running_MigrationScripts/Versioning_The_Database.cs b/grate.unittests/SqlServerCaseSensitive/Running_MigrationScripts/Versioning_The_Database.cs index ac04af59..8d88ba4a 100644 --- a/grate.unittests/SqlServerCaseSensitive/Running_MigrationScripts/Versioning_The_Database.cs +++ b/grate.unittests/SqlServerCaseSensitive/Running_MigrationScripts/Versioning_The_Database.cs @@ -6,7 +6,7 @@ namespace grate.unittests.SqlServerCaseSensitive.Running_MigrationScripts [TestFixture] [Category("SqlServerCaseSensitive")] // ReSharper disable once InconsistentNaming - public class Versioning_The_Database : Generic.Running_MigrationScripts.Versioning_The_Database + public class Versioning_The_Database : grate.unittests.SqlServer.Running_MigrationScripts.Versioning_The_Database { protected override IGrateTestContext Context => GrateTestContext.SqlServerCaseSensitive; } diff --git a/grate/Migration/AnsiSqlDatabase.cs b/grate/Migration/AnsiSqlDatabase.cs index 72f38ec1..3930f827 100644 --- a/grate/Migration/AnsiSqlDatabase.cs +++ b/grate/Migration/AnsiSqlDatabase.cs @@ -88,7 +88,7 @@ private async Task ExistingOrDefault(string schemaName, string tableName protected abstract DbConnection GetSqlConnection(string? connectionString); protected DbConnection AdminConnection => _adminConnection ??= GetSqlConnection(AdminConnectionString); - + protected DbConnection Connection => _connection ??= GetSqlConnection(ConnectionString); public DbConnection ActiveConnection { protected get; set; } = default!; @@ -129,8 +129,8 @@ protected async Task RunInAutonomousTransaction(string? connec { TResult res; using (var s = new TransactionScope( - TransactionScopeOption.Suppress, - new TransactionOptions() { IsolationLevel = IsolationLevel.ReadUncommitted} , + TransactionScopeOption.Suppress, + new TransactionOptions() { IsolationLevel = IsolationLevel.ReadUncommitted }, TransactionScopeAsyncFlowOption.Enabled)) { await using (var connection = GetSqlConnection(connectionString)) @@ -143,7 +143,7 @@ protected async Task RunInAutonomousTransaction(string? connec } return res; } - + protected async Task RunInAutonomousTransaction(string? connectionString, Func func) { using (var s = new TransactionScope(TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled)) @@ -157,7 +157,7 @@ protected async Task RunInAutonomousTransaction(string? connectionString, Func RunSchemaExists() { string sql = $"SELECT s.SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA s WHERE s.SCHEMA_NAME = '{SchemaName}'"; var res = await ExecuteScalarAsync(ActiveConnection, sql); - return res == SchemaName; + return res != null; // #230: If the server found a record that's good enough for us } // TODO: Change MySQL/MariaDB from using schemas to using grate_ prefix @@ -379,7 +379,7 @@ ALTER TABLE {VersionTable} string existsSql = ExistsSql(tableSchema, fullTableName); var res = await ExecuteScalarAsync(ActiveConnection, existsSql); - + var name = (!DBNull.Value.Equals(res) && res is not null) ? (string) res : null; var prefix = SupportsSchemas ? string.Empty : _syntax.TableWithSchema(schemaName, string.Empty); @@ -487,27 +487,27 @@ public void Rollback() public async Task RunSql(string sql, ConnectionType connectionType, TransactionHandling transactionHandling) { - Logger.LogTrace("[SQL] Running (on connection '{ConnType}', transaction handling '{TransactionHandling}'): \n{Sql}", - connectionType.ToString(), + Logger.LogTrace("[SQL] Running (on connection '{ConnType}', transaction handling '{TransactionHandling}'): \n{Sql}", + connectionType.ToString(), transactionHandling, sql); int? timeout = GetTimeout(connectionType); var connection = GetDbConnection(connectionType); - + await ExecuteNonQuery(connection, sql, timeout); } - - private DbConnection GetDbConnection(ConnectionType connectionType) => + + private DbConnection GetDbConnection(ConnectionType connectionType) => connectionType switch { ConnectionType.Default => ActiveConnection, ConnectionType.Admin => AdminConnection, _ => throw new UnknownConnectionType(connectionType) }; - - private int? GetTimeout(ConnectionType connectionType) => + + private int? GetTimeout(ConnectionType connectionType) => connectionType switch { ConnectionType.Default => Config?.CommandTimeout, diff --git a/grate/Migration/PostgreSqlDatabase.cs b/grate/Migration/PostgreSqlDatabase.cs index 1dfb5afc..a402d1dd 100644 --- a/grate/Migration/PostgreSqlDatabase.cs +++ b/grate/Migration/PostgreSqlDatabase.cs @@ -8,7 +8,7 @@ namespace grate.Migration; public class PostgreSqlDatabase : AnsiSqlDatabase { - public PostgreSqlDatabase(ILogger logger) + public PostgreSqlDatabase(ILogger logger) : base(logger, new PostgreSqlSyntax()) { } @@ -20,4 +20,18 @@ public override Task RestoreDatabase(string backupPath) { throw new System.NotImplementedException("Restoring a database from file is not currently supported for Postgresql."); } + + protected override string ExistsSql(string tableSchema, string fullTableName) + { + // For #230. Postgres tables are lowercase by default unless you quote them when created, which we do. We _don't_ quote the schema though, so it will always be lowercase + // Ensure the table check uses the lowercase version of anything we're passed, as that's what we would have created. + return base.ExistsSql(tableSchema.ToLower(), fullTableName); + } + + protected override string ExistsSql(string tableSchema, string fullTableName, string columnName) + { + // For #230. Postgres tables are lowercase by default unless you quote them when created, which we do. We _don't_ quote the schema though, so it will always be lowercase + // Ensure the table check uses the lowercase version of anything we're passed, as that's what we would have created. + return base.ExistsSql(tableSchema.ToLower(), fullTableName, columnName); + } } diff --git a/grate/Migration/SqLiteDatabase.cs b/grate/Migration/SqLiteDatabase.cs index 25f1d607..a04da752 100644 --- a/grate/Migration/SqLiteDatabase.cs +++ b/grate/Migration/SqLiteDatabase.cs @@ -10,9 +10,9 @@ namespace grate.Migration; public class SqliteDatabase : AnsiSqlDatabase { private static readonly SqliteSyntax Syntax = new(); - - - public SqliteDatabase(ILogger logger) + + + public SqliteDatabase(ILogger logger) : base(logger, Syntax) { } @@ -47,7 +47,7 @@ public override Task DropDatabase() { File.Delete(db); } - + return Task.CompletedTask; }