From 18ec09d23b34ff9a8e96915cb062d89161df0df5 Mon Sep 17 00:00:00 2001 From: Rachel Ambler <4251594-RachelAmbler@users.noreply.gitlab.com> Date: Sun, 11 Sep 2022 12:40:57 -0400 Subject: [PATCH 01/63] Fixes #237 Fixes some typos with AnyTime (also one for EveryTime and also keeep -> keep) --- docs/ConfigurationOptions/FolderConfiguration.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/ConfigurationOptions/FolderConfiguration.md b/docs/ConfigurationOptions/FolderConfiguration.md index 38c1d76b..5381db05 100644 --- a/docs/ConfigurationOptions/FolderConfiguration.md +++ b/docs/ConfigurationOptions/FolderConfiguration.md @@ -16,11 +16,11 @@ which might be a good starting point if you have no special requirements. grate works with three different folder types: -| Folder/script type | Explanation | More info | -| ------ | ------- |------- | -| One-time scripts | These are scripts that are run **exactly once** per database, and never again. | [One time scripts](../ScriptTypes/OneTimeScripts.md#one-time-scripts) | -| Anytime Scripts | These scripts are run **any time they're changed** | [Anytime scripts](../ScriptTypes/AnytimeScripts.md#anytime-scripts) | -| Everytime Scripts | These scripts are run (you guessed it) **every time** grate executes :) | [Everytime scripts](../ScriptTypes/EverytimeScripts.md#everytime-scripts) | +| Folder/script type | Name | Explanation | More info | +| ------ |----| ------- |------- | +| One-time scripts | Once | These are scripts that are run **exactly once** per database, and never again. | [One time scripts](../ScriptTypes/OneTimeScripts.md#one-time-scripts) | +| Anytime Scripts | AnyTime | These scripts are run **any time they're changed** | [Anytime scripts](../ScriptTypes/AnytimeScripts.md#anytime-scripts) | +| Everytime Scripts | EveryTime | These scripts are run (you guessed it) **every time** grate executes :) | [Everytime scripts](../ScriptTypes/EverytimeScripts.md#everytime-scripts) | ## Specifying a custom folder configuration @@ -81,7 +81,7 @@ The properties you can set per folder, are: | ------ | ------- | ------- | ------- | | Name | the key/name you wish to give to the folder | (doesn't matter if path is specified) | _(none)_ | | Path | the relative path of the folder, relative to the --sqlfilesdirectory parameter. | Any relative path | the **Name** specified above. -| Type | the type of the migration | Once, EveryTime, Anytime | Once | +| Type | the type of the migration | Once, EveryTime, AnyTime | Once | | ConnectionType | whether to run on the default connection, or on the admin | Default, Admin | Default | | TransactionHandling | whether to be part of the transaction (if running the migration in a transaction), or run the script in an autonomous transaction, so that it is always run, even on a rollback | Default, Autonomous | Default | @@ -96,7 +96,7 @@ Example: or ``` ---folders folder1=Once;folder2=Everytime;folder3=Anytime +--folders folder1=Once;folder2=EveryTime;folder3=AnyTime ``` the last one will expect the folders to be named `folder1`, `folder2`, and `folder3`, @@ -116,7 +116,7 @@ folders, should you wish so. Simply specify the folders you want to override in the `--folders` parameter. The ones you don't mention, will remain configured as default. -An example, if you want to use a folder `tables` to keeep you `up` scripts in, use the following argument to grate: +An example, if you want to use a folder `tables` to keep you `up` scripts in, use the following argument to grate: ```bash $ grate --folders up=tables From d75440ac5e4d355bf2650ff3ed7f79e4d9555064 Mon Sep 17 00:00:00 2001 From: Rachel Ambler <1454901+RachelAmbler@users.noreply.github.com> Date: Wed, 14 Sep 2022 16:57:26 -0400 Subject: [PATCH 02/63] Fixes #235 - (Maria DB -> Oracle) (#240) Co-authored-by: Rachel Ambler <4251594-RachelAmbler@users.noreply.gitlab.com> --- grate/Migration/OracleDatabase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grate/Migration/OracleDatabase.cs b/grate/Migration/OracleDatabase.cs index f6f7080c..940f28fa 100644 --- a/grate/Migration/OracleDatabase.cs +++ b/grate/Migration/OracleDatabase.cs @@ -73,7 +73,7 @@ protected override async Task CreateScriptsRunErrorsTable() public override Task RestoreDatabase(string backupPath) { - throw new System.NotImplementedException("Restoring a database from file is not currently supported for Maria DB."); + throw new System.NotImplementedException("Restoring a database from file is not currently supported for Oracle."); } protected override async Task CreateVersionTable() From d67ff0eed1ffc09cb2bf06fc5db852c83e5618d8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Oct 2022 19:26:24 +0200 Subject: [PATCH 03/63] Bump Microsoft.NET.Test.Sdk from 17.3.1 to 17.3.2 (#254) Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 17.3.1 to 17.3.2. - [Release notes](https://github.com/microsoft/vstest/releases) - [Commits](https://github.com/microsoft/vstest/compare/v17.3.1...v17.3.2) --- updated-dependencies: - dependency-name: Microsoft.NET.Test.Sdk dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- grate.unittests/grate.unittests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grate.unittests/grate.unittests.csproj b/grate.unittests/grate.unittests.csproj index c03b80fd..97f014f9 100644 --- a/grate.unittests/grate.unittests.csproj +++ b/grate.unittests/grate.unittests.csproj @@ -7,7 +7,7 @@ - + From 92b4706b359ceeac36c98af79f7cc84575110283 Mon Sep 17 00:00:00 2001 From: "Erik A. Brandstadmoen" Date: Sun, 2 Oct 2022 20:31:39 +0200 Subject: [PATCH 04/63] Restrict max CPU count to 2 (#257) --- .github/workflows/build.yml | 2 +- .github/workflows/ci.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7b67b84b..36285c79 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -270,7 +270,7 @@ jobs: dotnet-version: 6.0.x include-prerelease: false - name: Test - run: dotnet test --filter FullyQualifiedName~grate.unittests.${{ matrix.category }} -c Release --logger:"trx;LogFilePath=test-results-${{ matrix.category }}.xml" + run: dotnet test --filter FullyQualifiedName~grate.unittests.${{ matrix.category }} -c Release --logger:"trx;LogFilePath=test-results-${{ matrix.category }}.xml" -- -MaxCpuCount 2 # run: dotnet test --verbosity Normal -c Release --logger "trx;LogFileName=/tmp/test-results/grate.unittests.trx" env: LogLevel: Warning diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 450bbd69..45d55253 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -92,7 +92,7 @@ jobs: dotnet-version: 6.0.x include-prerelease: false - name: Test - run: dotnet vstest --TestCaseFilter:"FullyQualifiedName~grate.unittests.${{ matrix.category }}" bin/grate.unittests.dll --logger:"trx;LogFileName=test-results-${{ matrix.category }}.xml" + run: dotnet test --TestCaseFilter:"FullyQualifiedName~grate.unittests.${{ matrix.category }}" bin/grate.unittests.dll --logger:"trx;LogFileName=test-results-${{ matrix.category }}.xml" -- -MaxCpuCount 2 env: LogLevel: Warning TZ: UTC From d683cc2b8f4b8bfb5d79e6424896a8bb98520804 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Oct 2022 07:58:07 +0200 Subject: [PATCH 05/63] Bump docker/login-action from 2.0.0 to 2.1.0 (#263) Bumps [docker/login-action](https://github.com/docker/login-action) from 2.0.0 to 2.1.0. - [Release notes](https://github.com/docker/login-action/releases) - [Commits](https://github.com/docker/login-action/compare/49ed152c8eca782a232dede0303416e8f356c37b...f4ef78c080cd8ba55a85445d5b36e214a81df20a) --- updated-dependencies: - dependency-name: docker/login-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 36285c79..80b8d0f0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -164,7 +164,7 @@ jobs: - name: Log in to the Container registry - uses: docker/login-action@49ed152c8eca782a232dede0303416e8f356c37b + uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a with: #registry: ${{ env.REGISTRY }} username: ${{ github.repository_owner}} From b8f4c96ef3aa8b6651880982391f1a0066df3bf3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Oct 2022 08:00:45 +0200 Subject: [PATCH 06/63] Bump docker/build-push-action from 3.1.1 to 3.2.0 (#262) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 3.1.1 to 3.2.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/c84f38281176d4c9cdb1626ffafcd6b3911b5d94...c56af957549030174b10d6867f20e78cfd7debc5) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 80b8d0f0..006bf89d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -184,7 +184,7 @@ jobs: - name: Build and push Docker image - uses: docker/build-push-action@c84f38281176d4c9cdb1626ffafcd6b3911b5d94 + uses: docker/build-push-action@c56af957549030174b10d6867f20e78cfd7debc5 with: context: ./installers/docker/ push: true From cbd90cf88274c5bbf48d1e025b98ed728049ee00 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Oct 2022 08:01:11 +0200 Subject: [PATCH 07/63] Bump docker/metadata-action from 4.0.1 to 4.1.0 (#261) Bumps [docker/metadata-action](https://github.com/docker/metadata-action) from 4.0.1 to 4.1.0. - [Release notes](https://github.com/docker/metadata-action/releases) - [Commits](https://github.com/docker/metadata-action/compare/69f6fc9d46f2f8bf0d5491e4aabe0bb8c6a4678a...12cce9efe0d49980455aaaca9b071c0befcdd702) --- updated-dependencies: - dependency-name: docker/metadata-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 006bf89d..6d31df8e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -172,7 +172,7 @@ jobs: - name: Extract metadata (tags, labels) for Docker id: meta - uses: docker/metadata-action@69f6fc9d46f2f8bf0d5491e4aabe0bb8c6a4678a + uses: docker/metadata-action@12cce9efe0d49980455aaaca9b071c0befcdd702 with: tags: | type=semver,pattern={{version}} From 76147a752845c1918faf39fd9fbc20e9cb47fe1d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Oct 2022 08:01:30 +0200 Subject: [PATCH 08/63] Bump gittools/actions from 0.9.13 to 0.9.14 (#260) Bumps [gittools/actions](https://github.com/gittools/actions) from 0.9.13 to 0.9.14. - [Release notes](https://github.com/gittools/actions/releases) - [Commits](https://github.com/gittools/actions/compare/v0.9.13...v0.9.14) --- updated-dependencies: - dependency-name: gittools/actions dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6d31df8e..eee44781 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -30,12 +30,12 @@ jobs: with: fetch-depth: 0 - name: Install GitVersion - uses: gittools/actions/gitversion/setup@v0.9.13 + uses: gittools/actions/gitversion/setup@v0.9.14 with: versionSpec: '5.x' - name: Determine Version id: gitversion - uses: gittools/actions/gitversion/execute@v0.9.13 + uses: gittools/actions/gitversion/execute@v0.9.14 build-netcore-tool: needs: set-version-number From 4dd2b129d8dae42550ba3fc18f18053f306ed690 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Oct 2022 08:01:56 +0200 Subject: [PATCH 09/63] Bump Microsoft.Data.SqlClient from 5.0.0 to 5.0.1 (#259) Bumps [Microsoft.Data.SqlClient](https://github.com/dotnet/sqlclient) from 5.0.0 to 5.0.1. - [Release notes](https://github.com/dotnet/sqlclient/releases) - [Changelog](https://github.com/dotnet/SqlClient/blob/main/CHANGELOG.md) - [Commits](https://github.com/dotnet/sqlclient/commits) --- updated-dependencies: - dependency-name: Microsoft.Data.SqlClient dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- grate.unittests/grate.unittests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grate.unittests/grate.unittests.csproj b/grate.unittests/grate.unittests.csproj index 97f014f9..02082791 100644 --- a/grate.unittests/grate.unittests.csproj +++ b/grate.unittests/grate.unittests.csproj @@ -14,7 +14,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all - + From 234c16cb280122b09a894d71677e0342231aa801 Mon Sep 17 00:00:00 2001 From: Tim Thompson Date: Thu, 13 Oct 2022 16:02:52 +1000 Subject: [PATCH 10/63] fix: Added tests using case sensitive Sql server, and fixed issues with INFORMATION_SCHEMA (#251) --- grate.unittests/Generic/GenericDatabase.cs | 21 +------ grate.unittests/SqlServer/Database.cs | 25 +++++++- .../SqlServerCaseSensitive/Database.cs | 13 ++++ .../SqlServerCaseSensitive/DockerContainer.cs | 12 ++++ .../SqlServerCaseSensitive/MigrationTables.cs | 12 ++++ .../Anytime_scripts.cs | 13 ++++ .../Running_MigrationScripts/DropDatabase.cs | 12 ++++ .../Environment_scripts.cs | 13 ++++ .../Everytime_scripts.cs | 13 ++++ .../Failing_Scripts.cs | 14 +++++ .../One_time_scripts.cs | 13 ++++ .../Order_Of_Scripts.cs | 13 ++++ .../RestoreDatabase.cs | 60 +++++++++++++++++++ .../ScriptsRun_Table.cs | 9 +++ .../SqlServerScriptsBase.cs | 8 +++ .../Running_MigrationScripts/TokenScripts.cs | 12 ++++ .../Versioning_The_Database.cs | 13 ++++ .../SetupTestEnvironment.cs | 13 ++++ grate.unittests/TestContext.cs | 1 + .../SqlServerGrateTestContext.cs | 11 +++- grate/Migration/AnsiSqlDatabase.cs | 16 ++--- 21 files changed, 287 insertions(+), 30 deletions(-) create mode 100644 grate.unittests/SqlServerCaseSensitive/Database.cs create mode 100644 grate.unittests/SqlServerCaseSensitive/DockerContainer.cs create mode 100644 grate.unittests/SqlServerCaseSensitive/MigrationTables.cs create mode 100644 grate.unittests/SqlServerCaseSensitive/Running_MigrationScripts/Anytime_scripts.cs create mode 100644 grate.unittests/SqlServerCaseSensitive/Running_MigrationScripts/DropDatabase.cs create mode 100644 grate.unittests/SqlServerCaseSensitive/Running_MigrationScripts/Environment_scripts.cs create mode 100644 grate.unittests/SqlServerCaseSensitive/Running_MigrationScripts/Everytime_scripts.cs create mode 100644 grate.unittests/SqlServerCaseSensitive/Running_MigrationScripts/Failing_Scripts.cs create mode 100644 grate.unittests/SqlServerCaseSensitive/Running_MigrationScripts/One_time_scripts.cs create mode 100644 grate.unittests/SqlServerCaseSensitive/Running_MigrationScripts/Order_Of_Scripts.cs create mode 100644 grate.unittests/SqlServerCaseSensitive/Running_MigrationScripts/RestoreDatabase.cs create mode 100644 grate.unittests/SqlServerCaseSensitive/Running_MigrationScripts/ScriptsRun_Table.cs create mode 100644 grate.unittests/SqlServerCaseSensitive/Running_MigrationScripts/SqlServerScriptsBase.cs create mode 100644 grate.unittests/SqlServerCaseSensitive/Running_MigrationScripts/TokenScripts.cs create mode 100644 grate.unittests/SqlServerCaseSensitive/Running_MigrationScripts/Versioning_The_Database.cs create mode 100644 grate.unittests/SqlServerCaseSensitive/SetupTestEnvironment.cs diff --git a/grate.unittests/Generic/GenericDatabase.cs b/grate.unittests/Generic/GenericDatabase.cs index be6654e9..9e1114fa 100644 --- a/grate.unittests/Generic/GenericDatabase.cs +++ b/grate.unittests/Generic/GenericDatabase.cs @@ -91,23 +91,6 @@ public async Task Does_not_need_admin_connection_if_database_already_exists(stri 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 Task CreateDatabase(string db) => CreateDatabaseFromConnectionString(db, Context.ConnectionString(db)); @@ -172,9 +155,9 @@ protected virtual async Task> GetDatabases() protected virtual bool ThrowOnMissingDatabase => true; - private GrateMigrator GetMigrator(GrateConfiguration config) => Context.GetMigrator(config); + protected GrateMigrator GetMigrator(GrateConfiguration config) => Context.GetMigrator(config); - private GrateConfiguration GetConfiguration(string databaseName, bool createDatabase) + protected GrateConfiguration GetConfiguration(string databaseName, bool createDatabase) => GetConfiguration(databaseName, createDatabase, Context.AdminConnectionString); diff --git a/grate.unittests/SqlServer/Database.cs b/grate.unittests/SqlServer/Database.cs index 87dd47ae..7fde4f01 100644 --- a/grate.unittests/SqlServer/Database.cs +++ b/grate.unittests/SqlServer/Database.cs @@ -1,3 +1,6 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using FluentAssertions; using grate.unittests.TestInfrastructure; using NUnit.Framework; @@ -8,5 +11,23 @@ namespace grate.unittests.SqlServer; public class Database: Generic.GenericDatabase { protected override IGrateTestContext Context => GrateTestContext.SqlServer; - -} \ No newline at end of file + + [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, case insensitive 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()); + } + +} diff --git a/grate.unittests/SqlServerCaseSensitive/Database.cs b/grate.unittests/SqlServerCaseSensitive/Database.cs new file mode 100644 index 00000000..b211b35d --- /dev/null +++ b/grate.unittests/SqlServerCaseSensitive/Database.cs @@ -0,0 +1,13 @@ +using grate.unittests.TestInfrastructure; +using NUnit.Framework; + +namespace grate.unittests.SqlServerCaseSensitive +{ + [TestFixture] + [Category("SqlServerCaseSensitive")] + public class Database : Generic.GenericDatabase + { + protected override IGrateTestContext Context => GrateTestContext.SqlServerCaseSensitive; + + } +} diff --git a/grate.unittests/SqlServerCaseSensitive/DockerContainer.cs b/grate.unittests/SqlServerCaseSensitive/DockerContainer.cs new file mode 100644 index 00000000..7e1766ba --- /dev/null +++ b/grate.unittests/SqlServerCaseSensitive/DockerContainer.cs @@ -0,0 +1,12 @@ +using grate.unittests.TestInfrastructure; +using NUnit.Framework; + +namespace grate.unittests.SqlServerCaseSensitive +{ + [TestFixture] + [Category("SqlServerCaseSensitive")] + public class DockerContainer : Generic.GenericDockerContainer + { + protected override IGrateTestContext Context => GrateTestContext.SqlServerCaseSensitive; + } +} diff --git a/grate.unittests/SqlServerCaseSensitive/MigrationTables.cs b/grate.unittests/SqlServerCaseSensitive/MigrationTables.cs new file mode 100644 index 00000000..a7d1aefe --- /dev/null +++ b/grate.unittests/SqlServerCaseSensitive/MigrationTables.cs @@ -0,0 +1,12 @@ +using grate.unittests.TestInfrastructure; +using NUnit.Framework; + +namespace grate.unittests.SqlServerCaseSensitive +{ + [TestFixture] + [Category("SqlServerCaseSensitive")] + public class MigrationTables : Generic.GenericMigrationTables + { + protected override IGrateTestContext Context => GrateTestContext.SqlServerCaseSensitive; + } +} diff --git a/grate.unittests/SqlServerCaseSensitive/Running_MigrationScripts/Anytime_scripts.cs b/grate.unittests/SqlServerCaseSensitive/Running_MigrationScripts/Anytime_scripts.cs new file mode 100644 index 00000000..383dedaf --- /dev/null +++ b/grate.unittests/SqlServerCaseSensitive/Running_MigrationScripts/Anytime_scripts.cs @@ -0,0 +1,13 @@ +using grate.unittests.TestInfrastructure; +using NUnit.Framework; + +namespace grate.unittests.SqlServerCaseSensitive.Running_MigrationScripts +{ + [TestFixture] + [Category("SqlServerCaseSensitive")] + // ReSharper disable once InconsistentNaming + public class Anytime_scripts : Generic.Running_MigrationScripts.Anytime_scripts + { + protected override IGrateTestContext Context => GrateTestContext.SqlServerCaseSensitive; + } +} diff --git a/grate.unittests/SqlServerCaseSensitive/Running_MigrationScripts/DropDatabase.cs b/grate.unittests/SqlServerCaseSensitive/Running_MigrationScripts/DropDatabase.cs new file mode 100644 index 00000000..2da38f27 --- /dev/null +++ b/grate.unittests/SqlServerCaseSensitive/Running_MigrationScripts/DropDatabase.cs @@ -0,0 +1,12 @@ +using grate.unittests.TestInfrastructure; +using NUnit.Framework; + +namespace grate.unittests.SqlServerCaseSensitive.Running_MigrationScripts +{ + [TestFixture] + [Category("SqlServerCaseSensitive")] + public class DropDatabase : Generic.Running_MigrationScripts.DropDatabase + { + protected override IGrateTestContext Context => GrateTestContext.SqlServerCaseSensitive; + } +} diff --git a/grate.unittests/SqlServerCaseSensitive/Running_MigrationScripts/Environment_scripts.cs b/grate.unittests/SqlServerCaseSensitive/Running_MigrationScripts/Environment_scripts.cs new file mode 100644 index 00000000..589ccd43 --- /dev/null +++ b/grate.unittests/SqlServerCaseSensitive/Running_MigrationScripts/Environment_scripts.cs @@ -0,0 +1,13 @@ +using grate.unittests.TestInfrastructure; +using NUnit.Framework; + +namespace grate.unittests.SqlServerCaseSensitive.Running_MigrationScripts +{ + [TestFixture] + [Category("SqlServerCaseSensitive")] + // ReSharper disable once InconsistentNaming + public class Environment_scripts : Generic.Running_MigrationScripts.Environment_scripts + { + protected override IGrateTestContext Context => GrateTestContext.SqlServerCaseSensitive; + } +} diff --git a/grate.unittests/SqlServerCaseSensitive/Running_MigrationScripts/Everytime_scripts.cs b/grate.unittests/SqlServerCaseSensitive/Running_MigrationScripts/Everytime_scripts.cs new file mode 100644 index 00000000..c31af5ae --- /dev/null +++ b/grate.unittests/SqlServerCaseSensitive/Running_MigrationScripts/Everytime_scripts.cs @@ -0,0 +1,13 @@ +using grate.unittests.TestInfrastructure; +using NUnit.Framework; + +namespace grate.unittests.SqlServerCaseSensitive.Running_MigrationScripts +{ + [TestFixture] + [Category("SqlServerCaseSensitive")] + // ReSharper disable once InconsistentNaming + public class Everytime_scripts : Generic.Running_MigrationScripts.Everytime_scripts + { + protected override IGrateTestContext Context => GrateTestContext.SqlServerCaseSensitive; + } +} diff --git a/grate.unittests/SqlServerCaseSensitive/Running_MigrationScripts/Failing_Scripts.cs b/grate.unittests/SqlServerCaseSensitive/Running_MigrationScripts/Failing_Scripts.cs new file mode 100644 index 00000000..481bcf6b --- /dev/null +++ b/grate.unittests/SqlServerCaseSensitive/Running_MigrationScripts/Failing_Scripts.cs @@ -0,0 +1,14 @@ +using grate.unittests.TestInfrastructure; +using NUnit.Framework; + +namespace grate.unittests.SqlServerCaseSensitive.Running_MigrationScripts +{ + [TestFixture] + [Category("SqlServerCaseSensitive")] + // ReSharper disable once InconsistentNaming + public class Failing_Scripts : Generic.Running_MigrationScripts.Failing_Scripts + { + protected override IGrateTestContext Context => GrateTestContext.SqlServerCaseSensitive; + protected override string ExpectedErrorMessageForInvalidSql => "Incorrect syntax near 'TOP'."; + } +} diff --git a/grate.unittests/SqlServerCaseSensitive/Running_MigrationScripts/One_time_scripts.cs b/grate.unittests/SqlServerCaseSensitive/Running_MigrationScripts/One_time_scripts.cs new file mode 100644 index 00000000..2e78bab1 --- /dev/null +++ b/grate.unittests/SqlServerCaseSensitive/Running_MigrationScripts/One_time_scripts.cs @@ -0,0 +1,13 @@ +using grate.unittests.TestInfrastructure; +using NUnit.Framework; + +namespace grate.unittests.SqlServerCaseSensitive.Running_MigrationScripts +{ + [TestFixture] + [Category("SqlServerCaseSensitive")] + // ReSharper disable once InconsistentNaming + public class One_time_scripts : Generic.Running_MigrationScripts.One_time_scripts + { + protected override IGrateTestContext Context => GrateTestContext.SqlServerCaseSensitive; + } +} diff --git a/grate.unittests/SqlServerCaseSensitive/Running_MigrationScripts/Order_Of_Scripts.cs b/grate.unittests/SqlServerCaseSensitive/Running_MigrationScripts/Order_Of_Scripts.cs new file mode 100644 index 00000000..9b7a57b0 --- /dev/null +++ b/grate.unittests/SqlServerCaseSensitive/Running_MigrationScripts/Order_Of_Scripts.cs @@ -0,0 +1,13 @@ +using grate.unittests.TestInfrastructure; +using NUnit.Framework; + +namespace grate.unittests.SqlServerCaseSensitive.Running_MigrationScripts +{ + [TestFixture] + [Category("SqlServerCaseSensitive")] + // ReSharper disable once InconsistentNaming + public class Order_Of_Scripts : Generic.Running_MigrationScripts.Order_Of_Scripts + { + protected override IGrateTestContext Context => GrateTestContext.SqlServerCaseSensitive; + } +} diff --git a/grate.unittests/SqlServerCaseSensitive/Running_MigrationScripts/RestoreDatabase.cs b/grate.unittests/SqlServerCaseSensitive/Running_MigrationScripts/RestoreDatabase.cs new file mode 100644 index 00000000..b6252746 --- /dev/null +++ b/grate.unittests/SqlServerCaseSensitive/Running_MigrationScripts/RestoreDatabase.cs @@ -0,0 +1,60 @@ +using System.Linq; +using System.Threading.Tasks; +using Dapper; +using FluentAssertions; +using grate.Configuration; +using grate.unittests.TestInfrastructure; +using NUnit.Framework; + +namespace grate.unittests.SqlServerCaseSensitive.Running_MigrationScripts +{ + [TestFixture] + [Category("SqlServerCaseSensitive")] + public class RestoreDatabase : SqlServerScriptsBase + { + protected override IGrateTestContext Context => GrateTestContext.SqlServerCaseSensitive; + private readonly string _backupPath = "/var/opt/mssql/backup/test.bak"; + + [OneTimeSetUp] + public async Task RunBeforeTest() + { + await using (var conn = Context.CreateDbConnection("master")) + { + await conn.ExecuteAsync("use [master] CREATE DATABASE [test]"); + await conn.ExecuteAsync("use [test] CREATE TABLE dbo.Table_1 (column1 int NULL)"); + await conn.ExecuteAsync($"BACKUP DATABASE [test] TO DISK = '{_backupPath}'"); + await conn.ExecuteAsync("use [master] DROP DATABASE [test]"); + } + } + + [Test] + public async Task Ensure_database_gets_restored() + { + var db = TestConfig.RandomDatabase(); + + var parent = CreateRandomTempDirectory(); + var knownFolders = FoldersConfiguration.Default(null); + CreateDummySql(parent, knownFolders[KnownFolderKeys.Sprocs]); + + var restoreConfig = Context.GetConfiguration(db, parent, knownFolders) with + { + Restore = _backupPath + }; + + await using (var migrator = Context.GetMigrator(restoreConfig)) + { + await migrator.Migrate(); + } + + int[] results; + string sql = $"select count(1) from sys.tables where [name]='Table_1'"; + + await using (var conn = Context.CreateDbConnection(db)) + { + results = (await conn.QueryAsync(sql)).ToArray(); + } + + results.First().Should().Be(1); + } + } +} diff --git a/grate.unittests/SqlServerCaseSensitive/Running_MigrationScripts/ScriptsRun_Table.cs b/grate.unittests/SqlServerCaseSensitive/Running_MigrationScripts/ScriptsRun_Table.cs new file mode 100644 index 00000000..c5c5453b --- /dev/null +++ b/grate.unittests/SqlServerCaseSensitive/Running_MigrationScripts/ScriptsRun_Table.cs @@ -0,0 +1,9 @@ +using grate.unittests.TestInfrastructure; + +namespace grate.unittests.SqlServerCaseSensitive.Running_MigrationScripts +{ + public class ScriptsRun_Table : Generic.Running_MigrationScripts.ScriptsRun_Table + { + protected override IGrateTestContext Context => GrateTestContext.SqlServerCaseSensitive; + } +} diff --git a/grate.unittests/SqlServerCaseSensitive/Running_MigrationScripts/SqlServerScriptsBase.cs b/grate.unittests/SqlServerCaseSensitive/Running_MigrationScripts/SqlServerScriptsBase.cs new file mode 100644 index 00000000..7c81a13f --- /dev/null +++ b/grate.unittests/SqlServerCaseSensitive/Running_MigrationScripts/SqlServerScriptsBase.cs @@ -0,0 +1,8 @@ +using grate.unittests.Generic.Running_MigrationScripts; + +namespace grate.unittests.SqlServerCaseSensitive.Running_MigrationScripts +{ + public abstract class SqlServerScriptsBase : MigrationsScriptsBase + { + } +} diff --git a/grate.unittests/SqlServerCaseSensitive/Running_MigrationScripts/TokenScripts.cs b/grate.unittests/SqlServerCaseSensitive/Running_MigrationScripts/TokenScripts.cs new file mode 100644 index 00000000..c89d8015 --- /dev/null +++ b/grate.unittests/SqlServerCaseSensitive/Running_MigrationScripts/TokenScripts.cs @@ -0,0 +1,12 @@ +using grate.unittests.TestInfrastructure; +using NUnit.Framework; + +namespace grate.unittests.SqlServerCaseSensitive.Running_MigrationScripts +{ + [TestFixture] + [Category("SqlServerCaseSensitive")] + public class TokenScripts : Generic.Running_MigrationScripts.TokenScripts + { + protected override IGrateTestContext Context => GrateTestContext.SqlServerCaseSensitive; + } +} diff --git a/grate.unittests/SqlServerCaseSensitive/Running_MigrationScripts/Versioning_The_Database.cs b/grate.unittests/SqlServerCaseSensitive/Running_MigrationScripts/Versioning_The_Database.cs new file mode 100644 index 00000000..ac04af59 --- /dev/null +++ b/grate.unittests/SqlServerCaseSensitive/Running_MigrationScripts/Versioning_The_Database.cs @@ -0,0 +1,13 @@ +using grate.unittests.TestInfrastructure; +using NUnit.Framework; + +namespace grate.unittests.SqlServerCaseSensitive.Running_MigrationScripts +{ + [TestFixture] + [Category("SqlServerCaseSensitive")] + // ReSharper disable once InconsistentNaming + public class Versioning_The_Database : Generic.Running_MigrationScripts.Versioning_The_Database + { + protected override IGrateTestContext Context => GrateTestContext.SqlServerCaseSensitive; + } +} diff --git a/grate.unittests/SqlServerCaseSensitive/SetupTestEnvironment.cs b/grate.unittests/SqlServerCaseSensitive/SetupTestEnvironment.cs new file mode 100644 index 00000000..75fd4dfc --- /dev/null +++ b/grate.unittests/SqlServerCaseSensitive/SetupTestEnvironment.cs @@ -0,0 +1,13 @@ +using grate.unittests.TestInfrastructure; +using NUnit.Framework; + +namespace grate.unittests.SqlServerCaseSensitive +{ + [SetUpFixture] + [Category("SqlServerCaseSensitive")] + public class SetupTestEnvironment : Generic.SetupDockerTestEnvironment + { + protected override IGrateTestContext GrateTestContext => unittests.GrateTestContext.SqlServerCaseSensitive; + protected override IDockerTestContext DockerTestContext => unittests.GrateTestContext.SqlServerCaseSensitive; + } +} diff --git a/grate.unittests/TestContext.cs b/grate.unittests/TestContext.cs index 43f03bf9..8e0b04b0 100644 --- a/grate.unittests/TestContext.cs +++ b/grate.unittests/TestContext.cs @@ -5,6 +5,7 @@ namespace grate.unittests; public static class GrateTestContext { internal static readonly SqlServerGrateTestContext SqlServer = new(); + internal static readonly SqlServerGrateTestContext SqlServerCaseSensitive = new("Latin1_General_CS_AS"); //CS == Case Sensitive internal static readonly OracleGrateTestContext Oracle = new(); internal static readonly PostgreSqlGrateTestContext PostgreSql = new(); // ReSharper disable once InconsistentNaming diff --git a/grate.unittests/TestInfrastructure/SqlServerGrateTestContext.cs b/grate.unittests/TestInfrastructure/SqlServerGrateTestContext.cs index 518c102e..f204bc45 100644 --- a/grate.unittests/TestInfrastructure/SqlServerGrateTestContext.cs +++ b/grate.unittests/TestInfrastructure/SqlServerGrateTestContext.cs @@ -12,6 +12,13 @@ namespace grate.unittests.TestInfrastructure; class SqlServerGrateTestContext : TestContextBase, IGrateTestContext, IDockerTestContext { + + + public SqlServerGrateTestContext(string serverCollation) => ServerCollation = serverCollation; + + public SqlServerGrateTestContext(): this("Danish_Norwegian_CI_AS") + {} + public string AdminPassword { get; set; } = default!; public int? Port { get; set; } public override int? ContainerPort => 1433; @@ -25,7 +32,7 @@ class SqlServerGrateTestContext : TestContextBase, IGrateTestContext, IDockerTes }; public string DockerCommand(string serverName, string adminPassword) => - $"run -d --name {serverName} -e ACCEPT_EULA=Y -e SA_PASSWORD={adminPassword} -e MSSQL_PID=Developer -e MSSQL_COLLATION=Danish_Norwegian_CI_AS -P {DockerImage}"; + $"run -d --name {serverName} -e ACCEPT_EULA=Y -e SA_PASSWORD={adminPassword} -e MSSQL_PID=Developer -e MSSQL_COLLATION={ServerCollation} -P {DockerImage}"; public string AdminConnectionString => $"Data Source=localhost,{Port};Initial Catalog=master;User Id=sa;Password={AdminPassword};Encrypt=false;Pooling=false"; public string ConnectionString(string database) => $"Data Source=localhost,{Port};Initial Catalog={database};User Id=sa;Password={AdminPassword};Encrypt=false;Pooling=false"; @@ -57,4 +64,6 @@ public string DockerCommand(string serverName, string adminPassword) => }; public bool SupportsCreateDatabase => true; + + public string ServerCollation { get; } } diff --git a/grate/Migration/AnsiSqlDatabase.cs b/grate/Migration/AnsiSqlDatabase.cs index 8ff60797..7d12997c 100644 --- a/grate/Migration/AnsiSqlDatabase.cs +++ b/grate/Migration/AnsiSqlDatabase.cs @@ -254,7 +254,7 @@ private async Task CreateRunSchema() private async Task RunSchemaExists() { - string sql = $"SELECT s.schema_name FROM information_schema.schemata s WHERE s.schema_name = '{SchemaName}'"; + 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; } @@ -366,21 +366,21 @@ private async Task ColumnExists(string schemaName, string tableName, strin protected virtual string ExistsSql(string tableSchema, string fullTableName) { return $@" -SELECT * FROM information_schema.tables +SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE -table_schema = '{tableSchema}' AND -table_name = '{fullTableName}' +TABLE_SCHEMA = '{tableSchema}' AND +TABLE_NAME = '{fullTableName}' "; } protected virtual string ExistsSql(string tableSchema, string fullTableName, string columnName) { return $@" -SELECT * FROM information_schema.columns +SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE -table_schema = '{tableSchema}' AND -table_name = '{fullTableName}' AND -column_name = '{columnName}' +TABLE_SCHEMA = '{tableSchema}' AND +TABLE_NAME = '{fullTableName}' AND +COLUMN_NAME = '{columnName}' "; } From f1e3eab558e6e23698c78357d2ed36817c771a0c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Oct 2022 08:03:00 +0200 Subject: [PATCH 11/63] Bump actions/setup-dotnet from 2 to 3 (#258) Bumps [actions/setup-dotnet](https://github.com/actions/setup-dotnet) from 2 to 3. - [Release notes](https://github.com/actions/setup-dotnet/releases) - [Commits](https://github.com/actions/setup-dotnet/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/setup-dotnet dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 8 ++++---- .github/workflows/ci.yml | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index eee44781..7e206661 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -21,7 +21,7 @@ jobs: steps: - name: Setup .NET 6 - uses: actions/setup-dotnet@v2 + uses: actions/setup-dotnet@v3 with: dotnet-version: 6.0.x include-prerelease: false @@ -46,7 +46,7 @@ jobs: steps: - uses: actions/checkout@v3 - name: Setup .NET 6 - uses: actions/setup-dotnet@v2 + uses: actions/setup-dotnet@v3 with: dotnet-version: 6.0.x include-prerelease: false @@ -84,7 +84,7 @@ jobs: steps: - uses: actions/checkout@v3 - name: Setup .NET 6 - uses: actions/setup-dotnet@v2 + uses: actions/setup-dotnet@v3 with: dotnet-version: 6.0.x include-prerelease: false @@ -265,7 +265,7 @@ jobs: steps: - uses: actions/checkout@v3 - name: Setup .NET 6 - uses: actions/setup-dotnet@v2 + uses: actions/setup-dotnet@v3 with: dotnet-version: 6.0.x include-prerelease: false diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 45d55253..8e2f839f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,7 +22,7 @@ jobs: steps: - uses: actions/checkout@v3 - name: Setup .NET 6 - uses: actions/setup-dotnet@v2 + uses: actions/setup-dotnet@v3 with: dotnet-version: 6.0.x include-prerelease: false @@ -53,7 +53,7 @@ jobs: uses: actions/checkout@v3 - name: Setup .NET 6 - uses: actions/setup-dotnet@v2 + uses: actions/setup-dotnet@v3 with: dotnet-version: 6.0.x include-prerelease: false @@ -87,7 +87,7 @@ jobs: with: name: binaries - name: Setup .NET 6 - uses: actions/setup-dotnet@v2 + uses: actions/setup-dotnet@v3 with: dotnet-version: 6.0.x include-prerelease: false From 52484359d9c0ddeec7b0bdda254dca75c65b4485 Mon Sep 17 00:00:00 2001 From: "Erik A. Brandstadmoen" Date: Thu, 27 Oct 2022 21:36:20 +0200 Subject: [PATCH 12/63] BUmped PostgreSQL identifier from 14 -> 15 on latest (#271) --- .../TestInfrastructure/PostgreSqlGrateTestContext.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grate.unittests/TestInfrastructure/PostgreSqlGrateTestContext.cs b/grate.unittests/TestInfrastructure/PostgreSqlGrateTestContext.cs index 561684e4..4db5fb13 100644 --- a/grate.unittests/TestInfrastructure/PostgreSqlGrateTestContext.cs +++ b/grate.unittests/TestInfrastructure/PostgreSqlGrateTestContext.cs @@ -39,6 +39,6 @@ public string DockerCommand(string serverName, string adminPassword) => }; - public string ExpectedVersionPrefix => "PostgreSQL 14."; + public string ExpectedVersionPrefix => "PostgreSQL 15."; public bool SupportsCreateDatabase => true; } From f8596c6377e4e2f9dd68a9d51aa076689806b529 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 27 Oct 2022 21:52:49 +0200 Subject: [PATCH 13/63] Update FluentAssertions requirement from 6.7.* to 6.8.* (#267) Updates the requirements on [FluentAssertions](https://github.com/fluentassertions/fluentassertions) to permit the latest version. - [Release notes](https://github.com/fluentassertions/fluentassertions/releases) - [Changelog](https://github.com/fluentassertions/fluentassertions/blob/develop/AcceptApiChanges.ps1) - [Commits](https://github.com/fluentassertions/fluentassertions/compare/6.7.0...6.8.0) --- updated-dependencies: - dependency-name: FluentAssertions dependency-type: direct:production ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- grate.unittests/grate.unittests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grate.unittests/grate.unittests.csproj b/grate.unittests/grate.unittests.csproj index 02082791..5823afba 100644 --- a/grate.unittests/grate.unittests.csproj +++ b/grate.unittests/grate.unittests.csproj @@ -15,7 +15,7 @@ all - + From a144537477cf3290b72f2138beccc596f8864b94 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 27 Oct 2022 21:53:01 +0200 Subject: [PATCH 14/63] Bump docker/metadata-action from 4.1.0 to 4.1.1 (#266) Bumps [docker/metadata-action](https://github.com/docker/metadata-action) from 4.1.0 to 4.1.1. - [Release notes](https://github.com/docker/metadata-action/releases) - [Commits](https://github.com/docker/metadata-action/compare/12cce9efe0d49980455aaaca9b071c0befcdd702...57396166ad8aefe6098280995947635806a0e6ea) --- updated-dependencies: - dependency-name: docker/metadata-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7e206661..e0c31c1d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -172,7 +172,7 @@ jobs: - name: Extract metadata (tags, labels) for Docker id: meta - uses: docker/metadata-action@12cce9efe0d49980455aaaca9b071c0befcdd702 + uses: docker/metadata-action@57396166ad8aefe6098280995947635806a0e6ea with: tags: | type=semver,pattern={{version}} From a0f51e6a2cc532a58f6ad2af30895f0d045b67b5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Jan 2023 01:42:59 +0300 Subject: [PATCH 15/63] Bump gittools/actions from 0.9.14 to 0.9.15 (#272) Bumps [gittools/actions](https://github.com/gittools/actions) from 0.9.14 to 0.9.15. - [Release notes](https://github.com/gittools/actions/releases) - [Commits](https://github.com/gittools/actions/compare/v0.9.14...v0.9.15) --- updated-dependencies: - dependency-name: gittools/actions dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e0c31c1d..e5721219 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -30,12 +30,12 @@ jobs: with: fetch-depth: 0 - name: Install GitVersion - uses: gittools/actions/gitversion/setup@v0.9.14 + uses: gittools/actions/gitversion/setup@v0.9.15 with: versionSpec: '5.x' - name: Determine Version id: gitversion - uses: gittools/actions/gitversion/execute@v0.9.14 + uses: gittools/actions/gitversion/execute@v0.9.15 build-netcore-tool: needs: set-version-number From 363f31ab194d45d22f5100ae95259845c882ca5e Mon Sep 17 00:00:00 2001 From: OzBob Date: Wed, 4 Jan 2023 06:59:36 +0800 Subject: [PATCH 16/63] GettingStarted (#279) example url address updated to https://github.com/erikbra/grate/tree/main/examples --- docs/GettingStarted.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/GettingStarted.md b/docs/GettingStarted.md index d6ed376b..0776927f 100644 --- a/docs/GettingStarted.md +++ b/docs/GettingStarted.md @@ -28,7 +28,7 @@ But don't be fooled, there's power in this simplicity due to a couple of key fac ## Examples -There are samples included in source control in the [`/examples/`](https://github.com/erikbra/grate/examples) directory, have a look and a play in there for some more info. +There are samples included in source control in the [`/examples/`](https://github.com/erikbra/grate/tree/main/examples) directory, have a look and a play in there for some more info. ## Script Types From d6341e34cbd142b735c58332ca1115d665a1ba8b Mon Sep 17 00:00:00 2001 From: OzBob Date: Wed, 4 Jan 2023 07:00:29 +0800 Subject: [PATCH 17/63] up to v1.4.0 (#281) upgrade version of downloads to v1.4.0 in examples --- .github/workflows/grate-workflow.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/grate-workflow.yml b/.github/workflows/grate-workflow.yml index 8a9d0602..5dbad0f7 100644 --- a/.github/workflows/grate-workflow.yml +++ b/.github/workflows/grate-workflow.yml @@ -18,10 +18,10 @@ jobs: - uses: actions/checkout@v3 - name: Download grate - run: curl -sL https://github.com/erikbra/grate/releases/download/1.0.0/grate_1.0.0-1_amd64.deb -o /tmp/grate_1.0.0-1_amd64.deb + run: curl -sL https://github.com/erikbra/grate/releases/download/1.4.0/grate_1.4.0-1_amd64.deb -o /tmp/grate_1.4.0-1_amd64.deb - name: Install grate - run: sudo dpkg -i /tmp/grate_1.0.0-1_amd64.deb + run: sudo dpkg -i /tmp/grate_1.4.0-1_amd64.deb - name: Verify grate installation run: grate --help @@ -43,10 +43,10 @@ jobs: - uses: actions/checkout@v3 - name: Download grate - run: curl -sL https://github.com/erikbra/grate/releases/download/1.0.0/grate_1.0.0-1_amd64.deb -o /tmp/grate_1.0.0-1_amd64.deb + run: curl -sL https://github.com/erikbra/grate/releases/download/1.4.0/grate_1.4.0-1_amd64.deb -o /tmp/grate_1.4.0-1_amd64.deb - name: Install grate - run: sudo dpkg -i /tmp/grate_1.0.0-1_amd64.deb + run: sudo dpkg -i /tmp/grate_1.4.0-1_amd64.deb - name: Verify grate installation run: grate --help From 78edfcef49700584eee32e3600f7747648105328 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Jan 2023 02:03:05 +0300 Subject: [PATCH 18/63] Update NUnit3TestAdapter requirement from 4.2.* to 4.3.* (#273) Updates the requirements on [NUnit3TestAdapter](https://github.com/nunit/nunit3-vs-adapter) to permit the latest version. - [Release notes](https://github.com/nunit/nunit3-vs-adapter/releases) - [Commits](https://github.com/nunit/nunit3-vs-adapter/compare/V4.2.0...V4.3.0) --- updated-dependencies: - dependency-name: NUnit3TestAdapter dependency-type: direct:production ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- grate.unittests/grate.unittests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grate.unittests/grate.unittests.csproj b/grate.unittests/grate.unittests.csproj index 5823afba..e946d616 100644 --- a/grate.unittests/grate.unittests.csproj +++ b/grate.unittests/grate.unittests.csproj @@ -10,7 +10,7 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all From 398975685ffff972274d40be07f9915c37a5a5f7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Jan 2023 02:09:17 +0300 Subject: [PATCH 19/63] Update Microsoft.Extensions.DependencyInjection requirement (#275) Updates the requirements on [Microsoft.Extensions.DependencyInjection](https://github.com/dotnet/runtime) to permit the latest version. - [Release notes](https://github.com/dotnet/runtime/releases) - [Commits](https://github.com/dotnet/runtime/commits) --- updated-dependencies: - dependency-name: Microsoft.Extensions.DependencyInjection dependency-type: direct:production ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- grate/grate.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grate/grate.csproj b/grate/grate.csproj index 00d861b7..674a2835 100644 --- a/grate/grate.csproj +++ b/grate/grate.csproj @@ -22,7 +22,7 @@ up using modern .NET 6. - + From 0e2346d3d8a1e0a37f028501ecee872201bc7989 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Jan 2023 02:14:26 +0300 Subject: [PATCH 20/63] Update Microsoft.Extensions.Logging.Console requirement (#278) Updates the requirements on [Microsoft.Extensions.Logging.Console](https://github.com/dotnet/runtime) to permit the latest version. - [Release notes](https://github.com/dotnet/runtime/releases) - [Commits](https://github.com/dotnet/runtime/commits) --- updated-dependencies: - dependency-name: Microsoft.Extensions.Logging.Console dependency-type: direct:production ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- grate/grate.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grate/grate.csproj b/grate/grate.csproj index 674a2835..0ea8a1c6 100644 --- a/grate/grate.csproj +++ b/grate/grate.csproj @@ -21,7 +21,7 @@ up using modern .NET 6. - + From eb1bbdd347f608e4ff5e9b09f8377e83c7798892 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Jan 2023 02:14:42 +0300 Subject: [PATCH 21/63] Update Microsoft.Extensions.Hosting requirement from 6.0.* to 7.0.* (#276) Updates the requirements on [Microsoft.Extensions.Hosting](https://github.com/dotnet/runtime) to permit the latest version. - [Release notes](https://github.com/dotnet/runtime/releases) - [Commits](https://github.com/dotnet/runtime/commits) --- updated-dependencies: - dependency-name: Microsoft.Extensions.Hosting dependency-type: direct:production ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- grate/grate.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grate/grate.csproj b/grate/grate.csproj index 0ea8a1c6..b33c7fe6 100644 --- a/grate/grate.csproj +++ b/grate/grate.csproj @@ -23,7 +23,7 @@ up using modern .NET 6. - + From 80967f5dc7fd72c4ce4367061179d43423848be6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Jan 2023 02:15:51 +0300 Subject: [PATCH 22/63] Update Microsoft.Data.Sqlite requirement from 6.0.* to 7.0.* (#277) Updates the requirements on [Microsoft.Data.Sqlite](https://github.com/dotnet/efcore) to permit the latest version. - [Release notes](https://github.com/dotnet/efcore/releases) - [Commits](https://github.com/dotnet/efcore/commits) --- updated-dependencies: - dependency-name: Microsoft.Data.Sqlite dependency-type: direct:production ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- grate/grate.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grate/grate.csproj b/grate/grate.csproj index b33c7fe6..11b03f68 100644 --- a/grate/grate.csproj +++ b/grate/grate.csproj @@ -29,7 +29,7 @@ up using modern .NET 6. - + From 001b6dbdf35142ccc4908854944bc49a29a194eb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 8 Jan 2023 21:12:23 +0100 Subject: [PATCH 23/63] Update MySqlConnector requirement from 2.1.* to 2.2.* (#285) Updates the requirements on [MySqlConnector](https://github.com/mysql-net/MySqlConnector) to permit the latest version. - [Release notes](https://github.com/mysql-net/MySqlConnector/releases) - [Changelog](https://github.com/mysql-net/MySqlConnector/blob/master/docs/VersionHistory.md) - [Commits](https://github.com/mysql-net/MySqlConnector/compare/2.1.0...2.2.5) --- updated-dependencies: - dependency-name: MySqlConnector dependency-type: direct:production ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- grate.unittests/grate.unittests.csproj | 2 +- grate/grate.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/grate.unittests/grate.unittests.csproj b/grate.unittests/grate.unittests.csproj index e946d616..8d40c7b8 100644 --- a/grate.unittests/grate.unittests.csproj +++ b/grate.unittests/grate.unittests.csproj @@ -20,7 +20,7 @@ - + diff --git a/grate/grate.csproj b/grate/grate.csproj index 11b03f68..2ce533df 100644 --- a/grate/grate.csproj +++ b/grate/grate.csproj @@ -28,7 +28,7 @@ up using modern .NET 6. - + From cff2deea405f07e513e1b4d820939eb05d83fd66 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 8 Jan 2023 21:20:14 +0100 Subject: [PATCH 24/63] Update NUnit.Console requirement from 3.15.* to 3.16.* (#287) Updates the requirements on [NUnit.Console](https://github.com/nunit/nunit-console) to permit the latest version. - [Release notes](https://github.com/nunit/nunit-console/releases) - [Changelog](https://github.com/nunit/nunit-console/blob/main/CHANGES.txt) - [Commits](https://github.com/nunit/nunit-console/compare/3.15.0...3.16.0) --- updated-dependencies: - dependency-name: NUnit.Console dependency-type: direct:production ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- grate.unittests/grate.unittests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grate.unittests/grate.unittests.csproj b/grate.unittests/grate.unittests.csproj index 8d40c7b8..6fb80afd 100644 --- a/grate.unittests/grate.unittests.csproj +++ b/grate.unittests/grate.unittests.csproj @@ -9,7 +9,7 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all From e2e96c8d6346cf0563c6d3c6e0a71f8a67f53731 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 8 Jan 2023 21:20:34 +0100 Subject: [PATCH 25/63] Bump Microsoft.NET.Test.Sdk from 17.3.2 to 17.4.1 (#288) Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 17.3.2 to 17.4.1. - [Release notes](https://github.com/microsoft/vstest/releases) - [Changelog](https://github.com/microsoft/vstest/blob/main/docs/releases.md) - [Commits](https://github.com/microsoft/vstest/compare/v17.3.2...v17.4.1) --- updated-dependencies: - dependency-name: Microsoft.NET.Test.Sdk dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- grate.unittests/grate.unittests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grate.unittests/grate.unittests.csproj b/grate.unittests/grate.unittests.csproj index 6fb80afd..1e76498c 100644 --- a/grate.unittests/grate.unittests.csproj +++ b/grate.unittests/grate.unittests.csproj @@ -7,7 +7,7 @@ - + From 3a5f61e0cb8ac2bc87c5d4a1515e136e0a89d35c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 8 Jan 2023 21:25:45 +0100 Subject: [PATCH 26/63] Update Npgsql requirement from 6.0.* to 7.0.* (#286) Updates the requirements on [Npgsql](https://github.com/npgsql/npgsql) to permit the latest version. - [Release notes](https://github.com/npgsql/npgsql/releases) - [Commits](https://github.com/npgsql/npgsql/compare/v6.0.0...v7.0.1) --- updated-dependencies: - dependency-name: Npgsql dependency-type: direct:production ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- grate.unittests/grate.unittests.csproj | 2 +- grate/grate.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/grate.unittests/grate.unittests.csproj b/grate.unittests/grate.unittests.csproj index 1e76498c..6d3a15e7 100644 --- a/grate.unittests/grate.unittests.csproj +++ b/grate.unittests/grate.unittests.csproj @@ -19,7 +19,7 @@ - + diff --git a/grate/grate.csproj b/grate/grate.csproj index 2ce533df..f4f11792 100644 --- a/grate/grate.csproj +++ b/grate/grate.csproj @@ -27,7 +27,7 @@ up using modern .NET 6. - + From 7f0b3d83cd4edf914f608f312537f93c2e564467 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Feb 2023 07:37:28 +0100 Subject: [PATCH 27/63] Bump docker/build-push-action from 3.2.0 to 4.0.0 (#300) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 3.2.0 to 4.0.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/c56af957549030174b10d6867f20e78cfd7debc5...3b5e8027fcad23fda98b2e3ac259d8d67585f671) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e5721219..a1f5df55 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -184,7 +184,7 @@ jobs: - name: Build and push Docker image - uses: docker/build-push-action@c56af957549030174b10d6867f20e78cfd7debc5 + uses: docker/build-push-action@3b5e8027fcad23fda98b2e3ac259d8d67585f671 with: context: ./installers/docker/ push: true From 16a095c53139f29c0de345d132dcd8d6d025cdcb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Feb 2023 07:51:53 +0100 Subject: [PATCH 28/63] Bump Microsoft.Data.SqlClient from 5.0.1 to 5.1.0 (#299) Bumps [Microsoft.Data.SqlClient](https://github.com/dotnet/sqlclient) from 5.0.1 to 5.1.0. - [Release notes](https://github.com/dotnet/sqlclient/releases) - [Changelog](https://github.com/dotnet/SqlClient/blob/main/CHANGELOG.md) - [Commits](https://github.com/dotnet/sqlclient/compare/v5.0.1...v5.1.0) --- updated-dependencies: - dependency-name: Microsoft.Data.SqlClient dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- grate.unittests/grate.unittests.csproj | 2 +- grate/grate.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/grate.unittests/grate.unittests.csproj b/grate.unittests/grate.unittests.csproj index 6d3a15e7..ed4aacd0 100644 --- a/grate.unittests/grate.unittests.csproj +++ b/grate.unittests/grate.unittests.csproj @@ -14,7 +14,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all - + diff --git a/grate/grate.csproj b/grate/grate.csproj index f4f11792..6014e3a2 100644 --- a/grate/grate.csproj +++ b/grate/grate.csproj @@ -25,7 +25,7 @@ up using modern .NET 6. - + From 7b31ea2a32de4adfe6de9008d7ed8e7f9ca1d25b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Feb 2023 07:52:09 +0100 Subject: [PATCH 29/63] Bump docker/metadata-action from 4.1.1 to 4.3.0 (#297) Bumps [docker/metadata-action](https://github.com/docker/metadata-action) from 4.1.1 to 4.3.0. - [Release notes](https://github.com/docker/metadata-action/releases) - [Commits](https://github.com/docker/metadata-action/compare/57396166ad8aefe6098280995947635806a0e6ea...507c2f2dc502c992ad446e3d7a5dfbe311567a96) --- updated-dependencies: - dependency-name: docker/metadata-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a1f5df55..4b579eb6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -172,7 +172,7 @@ jobs: - name: Extract metadata (tags, labels) for Docker id: meta - uses: docker/metadata-action@57396166ad8aefe6098280995947635806a0e6ea + uses: docker/metadata-action@507c2f2dc502c992ad446e3d7a5dfbe311567a96 with: tags: | type=semver,pattern={{version}} From 30ff79955d078acbc263d9c32e347db597782fef Mon Sep 17 00:00:00 2001 From: Maxime Mangel Date: Mon, 13 Feb 2023 16:30:57 +0100 Subject: [PATCH 30/63] Using Markdown comment in issue templates so user doesn't need to remove the instructions lines manually (#293) * Using Markdown comment in bug_report.md template so user don't need to remove the instructions lines manually * Using Markdown comment in feature_request.md template so user doesn't need to remove the instructions lines manually --- .github/ISSUE_TEMPLATE/bug_report.md | 12 ++++++------ .github/ISSUE_TEMPLATE/feature_request.md | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 9a4c7685..7d766208 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -8,20 +8,20 @@ assignees: '' --- **Describe the bug** -A clear and concise description of what the bug is. + **To Reproduce** -Steps to reproduce the behavior + **Expected behavior** -A clear and concise description of what you expected to happen. + **Screenshots** -If applicable, add screenshots to help explain your problem. + **Desktop (please complete the following information):** - OS: [e.g. macOS, Linux] - - Version [e.g. 1.0.2] + - Version: [e.g. 1.0.2] **Additional context** -Add any other context about the problem here. + diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index bbcbbe7d..a6f653e0 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -8,13 +8,13 @@ assignees: '' --- **Is your feature request related to a problem? Please describe.** -A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + **Describe the solution you'd like** -A clear and concise description of what you want to happen. + **Describe alternatives you've considered** -A clear and concise description of any alternative solutions or features you've considered. + **Additional context** -Add any other context or screenshots about the feature request here. + From c64766f073b4600ea8aa03b98a42bfcf8022aa11 Mon Sep 17 00:00:00 2001 From: "Erik A. Brandstadmoen" Date: Thu, 23 Feb 2023 07:29:12 +0100 Subject: [PATCH 31/63] Chore: Fixed failing PostgreSQL test --- grate/Infrastructure/PostgreSqlSyntax.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/grate/Infrastructure/PostgreSqlSyntax.cs b/grate/Infrastructure/PostgreSqlSyntax.cs index 06b88189..06673024 100644 --- a/grate/Infrastructure/PostgreSqlSyntax.cs +++ b/grate/Infrastructure/PostgreSqlSyntax.cs @@ -24,6 +24,7 @@ public string StatementSeparatorRegex public string CreateSchema(string schemaName) => @$"CREATE SCHEMA ""{schemaName}"";"; public string CreateDatabase(string databaseName, string? _) => @$"CREATE DATABASE ""{databaseName}"""; public string DropDatabase(string databaseName) => @$"select pg_terminate_backend(pid) from pg_stat_activity where datname='{databaseName}'; + COMMIT; DROP DATABASE IF EXISTS ""{databaseName}"";"; public string TableWithSchema(string schemaName, string tableName) => $"{schemaName}.\"{tableName}\""; public string ReturnId => "RETURNING id;"; @@ -31,4 +32,4 @@ public string StatementSeparatorRegex public string Quote(string text) => $"\"{text}\""; public string PrimaryKeyConstraint(string tableName, string column) => $",\nCONSTRAINT PK_{tableName}_{column} PRIMARY KEY ({column})"; public string LimitN(string sql, int n) => sql + $"\nLIMIT {n}"; -} \ No newline at end of file +} From bd7da877655af221bd2a526840e464e5e7721b5b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Feb 2023 07:34:23 +0100 Subject: [PATCH 32/63] Bump Microsoft.NET.Test.Sdk from 17.4.1 to 17.5.0 (#306) Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 17.4.1 to 17.5.0. - [Release notes](https://github.com/microsoft/vstest/releases) - [Changelog](https://github.com/microsoft/vstest/blob/main/docs/releases.md) - [Commits](https://github.com/microsoft/vstest/compare/v17.4.1...v17.5.0) --- updated-dependencies: - dependency-name: Microsoft.NET.Test.Sdk dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- grate.unittests/grate.unittests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grate.unittests/grate.unittests.csproj b/grate.unittests/grate.unittests.csproj index ed4aacd0..0846c7ec 100644 --- a/grate.unittests/grate.unittests.csproj +++ b/grate.unittests/grate.unittests.csproj @@ -7,7 +7,7 @@ - + From 8a42e3d4d5484e1cdd5be3aeea854f996665ad1f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Feb 2023 07:44:26 +0100 Subject: [PATCH 33/63] Update NSubstitute requirement from 4.4.* to 5.0.* (#304) Updates the requirements on NSubstitute to permit the latest version. --- updated-dependencies: - dependency-name: NSubstitute dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- grate.unittests/grate.unittests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grate.unittests/grate.unittests.csproj b/grate.unittests/grate.unittests.csproj index 0846c7ec..644d22e0 100644 --- a/grate.unittests/grate.unittests.csproj +++ b/grate.unittests/grate.unittests.csproj @@ -16,7 +16,7 @@ - + From f37d2b4f3f55e105089a2091bc33aaab6992e812 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Feb 2023 08:05:41 +0100 Subject: [PATCH 34/63] Update FluentAssertions requirement from 6.8.* to 6.10.* (#303) Updates the requirements on [FluentAssertions](https://github.com/fluentassertions/fluentassertions) to permit the latest version. - [Release notes](https://github.com/fluentassertions/fluentassertions/releases) - [Changelog](https://github.com/fluentassertions/fluentassertions/blob/develop/AcceptApiChanges.ps1) - [Commits](https://github.com/fluentassertions/fluentassertions/compare/6.8.0...6.10.0) --- updated-dependencies: - dependency-name: FluentAssertions dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- grate.unittests/grate.unittests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grate.unittests/grate.unittests.csproj b/grate.unittests/grate.unittests.csproj index 644d22e0..f1ebe07d 100644 --- a/grate.unittests/grate.unittests.csproj +++ b/grate.unittests/grate.unittests.csproj @@ -15,7 +15,7 @@ all - + From d8121cf11ee33d51202024b6a56edd4054c3ab29 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 26 Mar 2023 20:35:04 +0200 Subject: [PATCH 35/63] build(deps): bump gittools/actions from 0.9.15 to 0.10.2 (#312) Bumps [gittools/actions](https://github.com/gittools/actions) from 0.9.15 to 0.10.2. - [Release notes](https://github.com/gittools/actions/releases) - [Commits](https://github.com/gittools/actions/compare/v0.9.15...v0.10.2) --- updated-dependencies: - dependency-name: gittools/actions dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4b579eb6..5258d408 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -30,12 +30,12 @@ jobs: with: fetch-depth: 0 - name: Install GitVersion - uses: gittools/actions/gitversion/setup@v0.9.15 + uses: gittools/actions/gitversion/setup@v0.10.2 with: versionSpec: '5.x' - name: Determine Version id: gitversion - uses: gittools/actions/gitversion/execute@v0.9.15 + uses: gittools/actions/gitversion/execute@v0.10.2 build-netcore-tool: needs: set-version-number From dbf0c2a6d2da2b4d76e389eb84375bb0f8100826 Mon Sep 17 00:00:00 2001 From: Tim Thompson Date: Mon, 27 Mar 2023 04:39:32 +1000 Subject: [PATCH 36/63] fix #282: Respect the users console background colours (#290) --- grate/Infrastructure/GrateConsoleFormatter.cs | 22 ++++++------ grate/Infrastructure/TextWriterExtensions.cs | 34 +++++++++---------- 2 files changed, 26 insertions(+), 30 deletions(-) diff --git a/grate/Infrastructure/GrateConsoleFormatter.cs b/grate/Infrastructure/GrateConsoleFormatter.cs index 780771a7..9b6af303 100644 --- a/grate/Infrastructure/GrateConsoleFormatter.cs +++ b/grate/Infrastructure/GrateConsoleFormatter.cs @@ -52,11 +52,11 @@ private static void CreateDefaultLogMessage(TextWriter textWriter, in Lo LogLevel logLevel = logEntry.LogLevel; ConsoleColors logLevelColors = GetLogLevelConsoleColors(logLevel); - textWriter.WriteColoredMessageLine(message, logLevelColors.Background, logLevelColors.Foreground); + textWriter.WriteColoredMessageLine(message, logLevelColors.Foreground); if (exception != null) { - textWriter.WriteColoredMessageLine(exception.ToString(), logLevelColors.Background, logLevelColors.Foreground); + textWriter.WriteColoredMessageLine(exception.ToString(), logLevelColors.Foreground); } textWriter.Flush(); } @@ -74,12 +74,12 @@ private static ConsoleColors GetLogLevelConsoleColors(LogLevel logLevel) // since just setting one can look bad on the users console. return logLevel switch { - LogLevel.Trace => new ConsoleColors(GrateConsoleColor.Foreground.DarkYellow, GrateConsoleColor.Background.Black), - LogLevel.Debug => new ConsoleColors(GrateConsoleColor.Foreground.DarkGray, GrateConsoleColor.Background.Black), - LogLevel.Information => new ConsoleColors(GrateConsoleColor.Foreground.Green, GrateConsoleColor.Background.Black), - LogLevel.Warning => new ConsoleColors(GrateConsoleColor.Foreground.Yellow, GrateConsoleColor.Background.Black), - LogLevel.Error => new ConsoleColors(GrateConsoleColor.Foreground.Black, GrateConsoleColor.Background.DarkRed), - LogLevel.Critical => new ConsoleColors(GrateConsoleColor.Foreground.White, GrateConsoleColor.Background.DarkRed), + LogLevel.Trace => new ConsoleColors(GrateConsoleColor.Foreground.DarkYellow), + LogLevel.Debug => new ConsoleColors(GrateConsoleColor.Foreground.DarkGray), + LogLevel.Information => new ConsoleColors(GrateConsoleColor.Foreground.Green), + LogLevel.Warning => new ConsoleColors(GrateConsoleColor.Foreground.Yellow), + LogLevel.Error => new ConsoleColors(GrateConsoleColor.Foreground.Black), + LogLevel.Critical => new ConsoleColors(GrateConsoleColor.Foreground.White), _ => ConsoleColors.None }; } @@ -87,15 +87,13 @@ private static ConsoleColors GetLogLevelConsoleColors(LogLevel logLevel) private readonly struct ConsoleColors { - public ConsoleColors(GrateConsoleColor foreground, GrateConsoleColor background) + public ConsoleColors(GrateConsoleColor foreground) { Foreground = foreground; - Background = background; } public GrateConsoleColor Foreground { get; } - public GrateConsoleColor Background { get; } public static ConsoleColors None => new(); } -} \ No newline at end of file +} diff --git a/grate/Infrastructure/TextWriterExtensions.cs b/grate/Infrastructure/TextWriterExtensions.cs index 1d6aeaf7..41198625 100644 --- a/grate/Infrastructure/TextWriterExtensions.cs +++ b/grate/Infrastructure/TextWriterExtensions.cs @@ -7,30 +7,30 @@ internal static class TextWriterExtensions { private static bool? _supportsAnsiColors; - public static void WriteColoredMessage(this TextWriter textWriter, string message, GrateConsoleColor background, GrateConsoleColor foreground) + public static void WriteColoredMessage(this TextWriter textWriter, string message, GrateConsoleColor foreground) { - SetColorsIfEnabled(textWriter, background, foreground); + SetColorsIfEnabled(textWriter, foreground); textWriter.Write(message); ResetColorsIfEnabled(textWriter); } - - public static void WriteColoredMessageLine(this TextWriter textWriter, string? message, GrateConsoleColor background, GrateConsoleColor foreground) + + public static void WriteColoredMessageLine(this TextWriter textWriter, string? message, GrateConsoleColor foreground) { - SetColorsIfEnabled(textWriter, background, foreground); + SetColorsIfEnabled(textWriter, foreground); textWriter.WriteLine(message); ResetColorsIfEnabled(textWriter); } - - - private static void SetColorsIfEnabled(TextWriter textWriter, GrateConsoleColor background, GrateConsoleColor foreground) + + + private static void SetColorsIfEnabled(TextWriter textWriter, GrateConsoleColor foreground) { if (!DisableAnsiColors) { - SetColors(textWriter, background.AnsiColorCode, foreground.AnsiColorCode); + SetColors(textWriter, foreground.AnsiColorCode); } } - + private static void ResetColorsIfEnabled(TextWriter textWriter) { if (!DisableAnsiColors) @@ -38,32 +38,30 @@ private static void ResetColorsIfEnabled(TextWriter textWriter) ResetColors(textWriter); } } - + private static void ResetColors(TextWriter textWriter) { textWriter.Write(GrateConsoleColor.Foreground.Default.AnsiColorCode); // reset to default foreground color - textWriter.Write(GrateConsoleColor.Background.Default.AnsiColorCode); // reset to the background color } - private static void SetColors(TextWriter textWriter, string backgroundColorAnsiCode, string foregroundColorAnsiCode) + private static void SetColors(TextWriter textWriter, string foregroundColorAnsiCode) { - textWriter.Write(backgroundColorAnsiCode); textWriter.Write(foregroundColorAnsiCode); } private static bool DisableAnsiColors => !SupportsAnsiColors || Console.IsOutputRedirected; - + private static bool SupportsAnsiColors => _supportsAnsiColors ??= GetSupportsAnsiColors(); private static bool GetSupportsAnsiColors() { try - // Calling Console.GetCursorPosition() sometimes fails if the console has not been written to yet + // Calling Console.GetCursorPosition() sometimes fails if the console has not been written to yet { lock (Console.Out) { var (oldPosition, _) = Console.GetCursorPosition(); - SetColors(Console.Out, GrateConsoleColor.Background.Gray.AnsiColorCode, GrateConsoleColor.Foreground.Blue.AnsiColorCode); + SetColors(Console.Out, GrateConsoleColor.Foreground.Blue.AnsiColorCode); var (currentPosition, yPos) = Console.GetCursorPosition(); ResetColors(Console.Out); @@ -84,4 +82,4 @@ private static bool GetSupportsAnsiColors() return true; } } -} \ No newline at end of file +} From 8e867e9ec0b2c56780c1f85d41606dbd1d8f9033 Mon Sep 17 00:00:00 2001 From: Matt Gallagher <46973220+mattgallagher92@users.noreply.github.com> Date: Sun, 26 Mar 2023 19:40:42 +0100 Subject: [PATCH 37/63] Clarify --adminconnectionstring purpose in docs (#309) --- docs/ConfigurationOptions/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/ConfigurationOptions/index.md b/docs/ConfigurationOptions/index.md index 6268c247..eda9bb1f 100644 --- a/docs/ConfigurationOptions/index.md +++ b/docs/ConfigurationOptions/index.md @@ -21,7 +21,7 @@ grate --connectionstring="Server=(localdb)\MSSQLLocalDB;Integrated Security=true | Option | Default | Purpose | | ------ | ------- | ------- | | -c
-cs
--connectionstring
--connstring <connectionstring> | - | **REQUIRED** You now provide an entire connection string. ServerName and Database are obsolete. | -| -a
-acs
-csa
--adminconnectionstring
--adminconnstring <adminconnectionstring> | Same as --connectionstring | The connection string for connecting to master, if you want to create the database. | +| -a
-acs
-csa
--adminconnectionstring
--adminconnstring <adminconnectionstring> | The value provided via --connectionstring, with the target database replaced with "master" | Used when creating a new database, rather than migrating an existing one. Must be set manually when creating new databases on DBMSs where there isn't guaranteed to be a database called "master". | | -f
--files
--sqlfilesdirectory <sqlfilesdirectory> | . (current directory) | The directory where your SQL scripts are located | | --folders | Default folders as described in [Getting started](../GettingStarted.md) | Folder configuration, see [Folder configuration](FolderConfiguration.md) for details. | | -o
--output
--outputPath <outputPath> | %LOCALAPPDATA%/grate | This is where everything related to the migration is stored. This includes any backups, all items that ran, permission dumps, logs, etc. | From 9e9ff37261be74d611ecc877bef28340cca3b6b1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 26 Mar 2023 20:41:27 +0200 Subject: [PATCH 38/63] build(deps): update NUnit3TestAdapter requirement from 4.3.* to 4.4.* (#308) Updates the requirements on [NUnit3TestAdapter](https://github.com/nunit/nunit3-vs-adapter) to permit the latest version. - [Release notes](https://github.com/nunit/nunit3-vs-adapter/releases) - [Commits](https://github.com/nunit/nunit3-vs-adapter/compare/V4.3.0...V4.4.0) --- updated-dependencies: - dependency-name: NUnit3TestAdapter dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- grate.unittests/grate.unittests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grate.unittests/grate.unittests.csproj b/grate.unittests/grate.unittests.csproj index f1ebe07d..6e44e7e7 100644 --- a/grate.unittests/grate.unittests.csproj +++ b/grate.unittests/grate.unittests.csproj @@ -10,7 +10,7 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all From 7c964bd01f55eea9d0fe1dc88afa86cb5bbcc81d Mon Sep 17 00:00:00 2001 From: "Erik A. Brandstadmoen" Date: Sun, 26 Mar 2023 21:32:53 +0200 Subject: [PATCH 39/63] Changed to using official Oracle docker image for testing (#291) * Changed to using official Oracle docker image for testing * Fixed Oracle version string in unit test * Added order by clause to non-deterministic test * Added ordering of select of scripts in test to ensure consistent ordering --- .../Running_MigrationScripts/Anytime_scripts.cs | 2 +- .../Running_MigrationScripts/Order_Of_Scripts.cs | 2 +- grate.unittests/TestInfrastructure/Docker.cs | 9 ++++++++- grate.unittests/TestInfrastructure/NUnitLogger.cs | 2 +- .../TestInfrastructure/OracleGrateTestContext.cs | 10 +++++----- .../TestInfrastructureSetupException.cs | 10 ++++++++++ grate/Infrastructure/GrateConsoleFormatter.cs | 2 +- 7 files changed, 27 insertions(+), 10 deletions(-) create mode 100644 grate.unittests/TestInfrastructure/TestInfrastructureSetupException.cs diff --git a/grate.unittests/Generic/Running_MigrationScripts/Anytime_scripts.cs b/grate.unittests/Generic/Running_MigrationScripts/Anytime_scripts.cs index fb3b1240..bf11d7a7 100644 --- a/grate.unittests/Generic/Running_MigrationScripts/Anytime_scripts.cs +++ b/grate.unittests/Generic/Running_MigrationScripts/Anytime_scripts.cs @@ -70,7 +70,7 @@ public async Task Are_run_again_if_changed_between_runs() } string[] scripts; - string sql = $"SELECT text_of_script FROM {Context.Syntax.TableWithSchema("grate", "ScriptsRun")}"; + string sql = $"SELECT text_of_script FROM {Context.Syntax.TableWithSchema("grate", "ScriptsRun")} ORDER BY id"; await using (var conn = Context.CreateDbConnection(db)) { diff --git a/grate.unittests/Generic/Running_MigrationScripts/Order_Of_Scripts.cs b/grate.unittests/Generic/Running_MigrationScripts/Order_Of_Scripts.cs index 604399af..24b4c3c5 100644 --- a/grate.unittests/Generic/Running_MigrationScripts/Order_Of_Scripts.cs +++ b/grate.unittests/Generic/Running_MigrationScripts/Order_Of_Scripts.cs @@ -27,7 +27,7 @@ public async Task Is_as_expected() } string[] scripts; - string sql = $"SELECT script_name FROM {Context.Syntax.TableWithSchema("grate", "ScriptsRun")}"; + string sql = $"SELECT script_name FROM {Context.Syntax.TableWithSchema("grate", "ScriptsRun")} ORDER BY id"; await using (var conn = Context.CreateDbConnection(db)) { diff --git a/grate.unittests/TestInfrastructure/Docker.cs b/grate.unittests/TestInfrastructure/Docker.cs index 71c8487e..701c66fa 100644 --- a/grate.unittests/TestInfrastructure/Docker.cs +++ b/grate.unittests/TestInfrastructure/Docker.cs @@ -23,7 +23,14 @@ public static class Docker //TestContext.Progress.WriteLine("find port: " + findPortArgs); var hostPortList = await RunDockerCommand(findPortArgs); - var hostPort = hostPortList.Split(" ", StringSplitOptions.RemoveEmptyEntries).First(); + var hostPort = hostPortList.Split(" ", StringSplitOptions.RemoveEmptyEntries).FirstOrDefault(); + + if (hostPort is null) + { + throw new TestInfrastructureSetupException( + $"Unable to get host port from docker run output '${hostPortList}'"); + } + return (serverName, int.Parse(hostPort)); } diff --git a/grate.unittests/TestInfrastructure/NUnitLogger.cs b/grate.unittests/TestInfrastructure/NUnitLogger.cs index d472355c..b9a63e90 100644 --- a/grate.unittests/TestInfrastructure/NUnitLogger.cs +++ b/grate.unittests/TestInfrastructure/NUnitLogger.cs @@ -20,7 +20,7 @@ public NUnitLogger(string name, LogLevel minimumLogLevel) _minimumLogLevel = minimumLogLevel; } - public IDisposable BeginScope(TState state) => default; //We don't have scoping support + public IDisposable BeginScope(TState state) where TState : notnull => default; //We don't have scoping support public bool IsEnabled(LogLevel logLevel) => _minimumLogLevel >= logLevel ; diff --git a/grate.unittests/TestInfrastructure/OracleGrateTestContext.cs b/grate.unittests/TestInfrastructure/OracleGrateTestContext.cs index ec555716..3f24c868 100644 --- a/grate.unittests/TestInfrastructure/OracleGrateTestContext.cs +++ b/grate.unittests/TestInfrastructure/OracleGrateTestContext.cs @@ -15,12 +15,12 @@ internal class OracleGrateTestContext : TestContextBase, IGrateTestContext, IDoc public override int? ContainerPort => 1521; public string DockerCommand(string serverName, string adminPassword) => - $"run -d --name {serverName} -e ORACLE_ENABLE_XDB=true -P oracleinanutshell/oracle-xe-11g:latest"; + $"run -d --name {serverName} -p 1521 -e ORACLE_ENABLE_XDB=true -e ORACLE_PWD={adminPassword} -P container-registry.oracle.com/database/express:21.3.0-xe"; - public string AdminConnectionString => $@"Data Source=localhost:{Port}/XE;User ID=SYSTEM;Password=oracle;Pooling=False"; - public string ConnectionString(string database) => $@"Data Source=localhost:{Port}/XE;User ID={database.ToUpper()};Password=oracle;Pooling=False"; - public string UserConnectionString(string database) => $@"Data Source=localhost:{Port}/XE;User ID={database.ToUpper()};Password=oracle;Pooling=False"; + public string AdminConnectionString => $@"Data Source=localhost:{Port}/XEPDB1;User ID=system;Password={AdminPassword};Pooling=False"; + public string ConnectionString(string database) => $@"Data Source=localhost:{Port}/XEPDB1;User ID={database.ToUpper()};Password={AdminPassword};Pooling=False"; + public string UserConnectionString(string database) => $@"Data Source=localhost:{Port}/XEPDB1;User ID={database.ToUpper()};Password={AdminPassword};Pooling=False"; public DbConnection GetDbConnection(string connectionString) => new OracleConnection(connectionString); @@ -40,6 +40,6 @@ public string DockerCommand(string serverName, string adminPassword) => SleepTwoSeconds = "sys.dbms_session.sleep(2);" }; - public string ExpectedVersionPrefix => "Oracle Database 11g Express Edition Release 11.2.0.2.0 - 64bit Production"; + public string ExpectedVersionPrefix => "Oracle Database 21c Express Edition Release 21.0.0.0.0 - Production"; public bool SupportsCreateDatabase => true; } diff --git a/grate.unittests/TestInfrastructure/TestInfrastructureSetupException.cs b/grate.unittests/TestInfrastructure/TestInfrastructureSetupException.cs new file mode 100644 index 00000000..e5cdf371 --- /dev/null +++ b/grate.unittests/TestInfrastructure/TestInfrastructureSetupException.cs @@ -0,0 +1,10 @@ +using System; + +namespace grate.unittests.TestInfrastructure; + +public class TestInfrastructureSetupException: Exception +{ + public TestInfrastructureSetupException(string message): base(message) + { + } +} diff --git a/grate/Infrastructure/GrateConsoleFormatter.cs b/grate/Infrastructure/GrateConsoleFormatter.cs index 9b6af303..43628339 100644 --- a/grate/Infrastructure/GrateConsoleFormatter.cs +++ b/grate/Infrastructure/GrateConsoleFormatter.cs @@ -21,7 +21,7 @@ public GrateConsoleFormatter(IOptionsMonitor? opt } } - public override void Write(in LogEntry logEntry, IExternalScopeProvider scopeProvider, TextWriter textWriter) + public override void Write(in LogEntry logEntry, IExternalScopeProvider? scopeProvider, TextWriter textWriter) { string? message = logEntry.Formatter?.Invoke(logEntry.State, logEntry.Exception); if (logEntry.Exception == null && message == null) From 010041f611ed4274113df5473e4da6de7df23f11 Mon Sep 17 00:00:00 2001 From: "Erik A. Brandstadmoen" Date: Sun, 26 Mar 2023 22:25:42 +0200 Subject: [PATCH 40/63] .NET 7 support (#313) Multi-target .NET6 and .NET7. Most relevant with the dotnet tool distribution, and the framework-dependent packages. Shouldn't make any difference with the self-contained binaries (which are, by definition, framework independent). Added a few more build architectures also (M1 builds for macOS) --- .github/workflows/build.yml | 26 +++++++++++--------------- .github/workflows/ci.yml | 17 ++++++----------- grate.unittests/grate.unittests.csproj | 1 + grate/grate.csproj | 3 ++- 4 files changed, 20 insertions(+), 27 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5258d408..c40712a9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -20,11 +20,10 @@ jobs: #is-release: 'true' steps: - - name: Setup .NET 6 + - name: Setup .NET 7 uses: actions/setup-dotnet@v3 with: - dotnet-version: 6.0.x - include-prerelease: false + dotnet-version: 7.0.x - name: Checkout uses: actions/checkout@v3 with: @@ -45,11 +44,10 @@ jobs: steps: - uses: actions/checkout@v3 - - name: Setup .NET 6 + - name: Setup .NET 7 uses: actions/setup-dotnet@v3 with: - dotnet-version: 6.0.x - include-prerelease: false + dotnet-version: 7.0.x - name: Restore dependencies run: dotnet restore - name: Build @@ -78,23 +76,22 @@ jobs: matrix: arch: [ "win-x64", "win-x86", "win-arm", "win-arm64", "alpine-x64", "linux-x64", "linux-arm", "linux-arm64", - "osx-x64" + "osx-x64", "osx.11.0-x64", "osx.11.0-arm64", "osx.12-x64", "osx.12-arm64", "osx.13-x64", "osx.13-arm64" ] steps: - uses: actions/checkout@v3 - - name: Setup .NET 6 + - name: Setup .NET 7 uses: actions/setup-dotnet@v3 with: - dotnet-version: 6.0.x - include-prerelease: false + dotnet-version: 7.0.x - name: Publish self-contained ${{ matrix.arch }} run: dotnet publish ./grate/grate.csproj -r ${{ matrix.arch }} -c release --self-contained -p:SelfContained=true -o ./publish/${{ matrix.arch }}/self-contained env: VERSION: ${{ needs.set-version-number.outputs.nuGetVersion }} - - name: Publish .NET 6 dependent ${{ matrix.arch }} + - name: Publish .NET 6/7 dependent ${{ matrix.arch }} run: dotnet publish ./grate/grate.csproj -r ${{ matrix.arch }} -c release --no-self-contained -o ./publish/${{ matrix.arch }}/dependent env: VERSION: ${{ needs.set-version-number.outputs.nuGetVersion }} @@ -122,7 +119,7 @@ jobs: if: ${{ needs.set-version-number.outputs.is-release == 'true' }} strategy: matrix: - arch: [ "win-x64" ] + arch: [ "win-x64", "win-arm64" ] steps: - uses: actions/checkout@v3 @@ -264,11 +261,10 @@ jobs: steps: - uses: actions/checkout@v3 - - name: Setup .NET 6 + - name: Setup .NET 7 uses: actions/setup-dotnet@v3 with: - dotnet-version: 6.0.x - include-prerelease: false + dotnet-version: 7.0.x - name: Test run: dotnet test --filter FullyQualifiedName~grate.unittests.${{ matrix.category }} -c Release --logger:"trx;LogFilePath=test-results-${{ matrix.category }}.xml" -- -MaxCpuCount 2 # run: dotnet test --verbosity Normal -c Release --logger "trx;LogFileName=/tmp/test-results/grate.unittests.trx" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8e2f839f..c47d6b2a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,11 +21,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - name: Setup .NET 6 + - name: Setup .NET 7 uses: actions/setup-dotnet@v3 with: - dotnet-version: 6.0.x - include-prerelease: false + dotnet-version: 7.0.x - name: Restore dependencies run: dotnet restore -r linux-x64 grate.unittests/grate.unittests.csproj - name: Build @@ -52,17 +51,14 @@ jobs: - name: Checkout repository uses: actions/checkout@v3 - - name: Setup .NET 6 + - name: Setup .NET 7 uses: actions/setup-dotnet@v3 with: - dotnet-version: 6.0.x - include-prerelease: false + dotnet-version: 7.0.x # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v2 - with: - languages: 'csharp' - name: Autobuild uses: github/codeql-action/autobuild@v2 @@ -86,11 +82,10 @@ jobs: uses: actions/download-artifact@v3 with: name: binaries - - name: Setup .NET 6 + - name: Setup .NET 7 uses: actions/setup-dotnet@v3 with: - dotnet-version: 6.0.x - include-prerelease: false + dotnet-version: 7.0.x - name: Test run: dotnet test --TestCaseFilter:"FullyQualifiedName~grate.unittests.${{ matrix.category }}" bin/grate.unittests.dll --logger:"trx;LogFileName=test-results-${{ matrix.category }}.xml" -- -MaxCpuCount 2 env: diff --git a/grate.unittests/grate.unittests.csproj b/grate.unittests/grate.unittests.csproj index 6e44e7e7..b45614ce 100644 --- a/grate.unittests/grate.unittests.csproj +++ b/grate.unittests/grate.unittests.csproj @@ -2,6 +2,7 @@ net6.0 + net6.0;net7.0 false enable diff --git a/grate/grate.csproj b/grate/grate.csproj index 6014e3a2..7b9318cd 100644 --- a/grate/grate.csproj +++ b/grate/grate.csproj @@ -3,6 +3,7 @@ Exe net6.0 + net6.0;net7.0 Embedded enable MIT @@ -12,7 +13,7 @@ grate - sql for the 20s grate is a no-code, low-fi database migration tool, inspired heavily by RoundhousE. It's written from the ground -up using modern .NET 6. +up using modern .NET 6/7. https://erikbra.github.io/grate/ https://github.com/erikbra/grate From 47cb030d5ef175d75aaff0a0c0f40c48cdd778f1 Mon Sep 17 00:00:00 2001 From: "Erik A. Brandstadmoen" Date: Mon, 27 Mar 2023 22:41:45 +0200 Subject: [PATCH 41/63] Make unit tests work again (#315) Unit tests stopped running after adding net7.0 as a build target --- .github/workflows/build.yml | 2 +- .github/workflows/ci.yml | 2 +- CONTRIBUTING.md | 2 +- grate.unittests/grate.unittests.csproj | 3 +-- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c40712a9..f67c3cb6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -266,7 +266,7 @@ jobs: with: dotnet-version: 7.0.x - name: Test - run: dotnet test --filter FullyQualifiedName~grate.unittests.${{ matrix.category }} -c Release --logger:"trx;LogFilePath=test-results-${{ matrix.category }}.xml" -- -MaxCpuCount 2 + run: dotnet test --filter "FullyQualifiedName~grate.unittests.${{ matrix.category }}"" -c Release --logger:"trx;LogFilePath=test-results-${{ matrix.category }}.xml" -- -MaxCpuCount 2 # run: dotnet test --verbosity Normal -c Release --logger "trx;LogFileName=/tmp/test-results/grate.unittests.trx" env: LogLevel: Warning diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c47d6b2a..8c193599 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -87,7 +87,7 @@ jobs: with: dotnet-version: 7.0.x - name: Test - run: dotnet test --TestCaseFilter:"FullyQualifiedName~grate.unittests.${{ matrix.category }}" bin/grate.unittests.dll --logger:"trx;LogFileName=test-results-${{ matrix.category }}.xml" -- -MaxCpuCount 2 + run: dotnet test --filter "FullyQualifiedName~grate.unittests.${{ matrix.category }}" bin/grate.unittests.dll --logger:"trx;LogFileName=test-results-${{ matrix.category }}.xml" -- -MaxCpuCount 2 env: LogLevel: Warning TZ: UTC diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e199189e..a6167e95 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -28,7 +28,7 @@ git clone https://github.com/erikbra/grate.git ``` > cd grate -> dotnet test +> dotnet test --framework net7.0 ``` ## Build a self-contained executable (if you want) diff --git a/grate.unittests/grate.unittests.csproj b/grate.unittests/grate.unittests.csproj index b45614ce..84d4e1e6 100644 --- a/grate.unittests/grate.unittests.csproj +++ b/grate.unittests/grate.unittests.csproj @@ -1,8 +1,7 @@ - net6.0 - net6.0;net7.0 + net7.0 false enable From 210dd9df2a5c48d8cb53d3c81b8b9e1c1978314e Mon Sep 17 00:00:00 2001 From: "Erik A. Brandstadmoen" Date: Mon, 27 Mar 2023 22:46:52 +0200 Subject: [PATCH 42/63] Fixed error in unit test running in CI --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f67c3cb6..305c9663 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -266,7 +266,7 @@ jobs: with: dotnet-version: 7.0.x - name: Test - run: dotnet test --filter "FullyQualifiedName~grate.unittests.${{ matrix.category }}"" -c Release --logger:"trx;LogFilePath=test-results-${{ matrix.category }}.xml" -- -MaxCpuCount 2 + run: dotnet test --filter "FullyQualifiedName~grate.unittests.${{ matrix.category }}" -c Release --logger:"trx;LogFilePath=test-results-${{ matrix.category }}.xml" -- -MaxCpuCount 2 # run: dotnet test --verbosity Normal -c Release --logger "trx;LogFileName=/tmp/test-results/grate.unittests.trx" env: LogLevel: Warning From 7444cdebeaf4562cf1e31d41382ca36941c5dc2a Mon Sep 17 00:00:00 2001 From: "Erik A. Brandstadmoen" Date: Tue, 28 Mar 2023 07:50:32 +0200 Subject: [PATCH 43/63] Bug #311: Do not output 'No sql run' in Dryrun mode (#316) --- grate.unittests/Basic/Migration.cs | 75 ++++++++++++++++++++++++++++++ grate/Migration/GrateMigrator.cs | 2 +- 2 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 grate.unittests/Basic/Migration.cs diff --git a/grate.unittests/Basic/Migration.cs b/grate.unittests/Basic/Migration.cs new file mode 100644 index 00000000..131de20f --- /dev/null +++ b/grate.unittests/Basic/Migration.cs @@ -0,0 +1,75 @@ +using System.IO; +using System.Threading.Tasks; +using grate.Configuration; +using grate.Infrastructure; +using grate.Migration; +using grate.unittests.TestInfrastructure; +using Microsoft.Extensions.Logging; +using NSubstitute; +using NUnit.Framework; + +namespace grate.unittests.Basic; + +public class Migration +{ + private readonly ILogger _logger; + + public Migration() + { + _logger = Substitute.For>(); + } + + [Test] + public async Task Does_not_output_no_sql_run_in_dryrun_mode() + { + var dbMigrator = GetDbMigrator(true); + var migrator = new GrateMigrator(_logger, dbMigrator); + await migrator.Migrate(); + _logger.DidNotReceive().LogInformation(" No sql run, either an empty folder, or all files run against destination previously."); + } + + [Test] + public async Task Outputs_no_sql_run_in_live_mode() + { + var dbMigrator = GetDbMigrator(false); + var migrator = new GrateMigrator(_logger, dbMigrator); + await migrator.Migrate(); + _logger.Received().LogInformation(" No sql run, either an empty folder, or all files run against destination previously."); + } + + protected static DirectoryInfo Wrap(DirectoryInfo root, string? subFolder) => + new DirectoryInfo(Path.Combine(root.ToString(), subFolder ?? "")); + + private static IDbMigrator GetDbMigrator(bool dryRun) + { + var dbMigrator = Substitute.For(); + dbMigrator.RunSql( + Arg.Any(), + Arg.Any(), + Arg.Any(), + Arg.Any(), + Arg.Any(), + Arg.Any(), + Arg.Any() + ).ReturnsForAnyArgs(false); + var parent = TestConfig.CreateRandomTempDirectory(); + var knownFolders = FoldersConfiguration.Default(); + + var path = Wrap(parent, knownFolders[KnownFolderKeys.Up]!.Path); + + var folder1 = new DirectoryInfo(Path.Combine(path.ToString(), "01_sub", "folder", "long", "way")); + string filename1 = "01_any_filename.sql"; + TestConfig.WriteContent(folder1, filename1, "Whatever"); + + var configuration = new GrateConfiguration() + { + NonInteractive = true, + SqlFilesDirectory = parent, + DryRun = dryRun + }; + dbMigrator.Configuration.Returns(configuration); + + return dbMigrator; + } + +} diff --git a/grate/Migration/GrateMigrator.cs b/grate/Migration/GrateMigrator.cs index ae4be1fc..b1e35f44 100644 --- a/grate/Migration/GrateMigrator.cs +++ b/grate/Migration/GrateMigrator.cs @@ -324,7 +324,7 @@ private async Task Process(DirectoryInfo root, MigrationsFolder folder, string c } } - if (!anySqlRun) + if (!anySqlRun && !_migrator.Configuration.DryRun) { _logger.LogInformation(" No sql run, either an empty folder, or all files run against destination previously."); } From 773d10b1dbc059d7e5b6f99cbf50737af45134b1 Mon Sep 17 00:00:00 2001 From: "Erik A. Brandstadmoen" Date: Tue, 28 Mar 2023 08:03:52 +0200 Subject: [PATCH 44/63] Bug #302: Clarify documentation when specifying multiple folders (#317) --- docs/ConfigurationOptions/FolderConfiguration.md | 5 ++++- grate/Commands/MigrateCommand.cs | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/ConfigurationOptions/FolderConfiguration.md b/docs/ConfigurationOptions/FolderConfiguration.md index 5381db05..cb911578 100644 --- a/docs/ConfigurationOptions/FolderConfiguration.md +++ b/docs/ConfigurationOptions/FolderConfiguration.md @@ -44,9 +44,12 @@ This would use the [default folder configuration](#default-folder-configuration) `ddl` folder for **up** scripts, in the `projections` folder for **views**, and in the `preparefordeploy` folder for **beforemigration** scripts. ``` ---folders up=ddl;views=projections;beforemigration=preparefordeploy +--folders 'up=ddl;views=projections;beforemigration=preparefordeploy' ``` +**NOTE:** Be sure to use quotes when specifying multiple folders in the argument, as many shells treat `;` as +a the "end this command" character, so everything after the `;` will not be part of the command line. + or ``` diff --git a/grate/Commands/MigrateCommand.cs b/grate/Commands/MigrateCommand.cs index adeea2c6..2210c406 100644 --- a/grate/Commands/MigrateCommand.cs +++ b/grate/Commands/MigrateCommand.cs @@ -96,7 +96,7 @@ private static Option Folders() => Example: - --folders up=ddl;views=projections;beforemigration=preparefordeploy + --folders 'up=ddl;views=projections;beforemigration=preparefordeploy' or From 6435cb1ee2986bc6501aa3e233ad52a11f615abf Mon Sep 17 00:00:00 2001 From: "Erik A. Brandstadmoen" Date: Tue, 28 Mar 2023 22:14:49 +0200 Subject: [PATCH 45/63] Issue #307: Set 'master' DB name per database type (#314) --- .../Basic_CommandLineParsing.cs | 16 ++++++++++++++++ .../OracleGrateTestContext.cs | 1 + .../TestInfrastructure/TestConfig.cs | 13 ++++++++++++- grate/Configuration/GrateConfiguration.cs | 19 ++++++++++++++++--- 4 files changed, 45 insertions(+), 4 deletions(-) diff --git a/grate.unittests/Basic/CommandLineParsing/Basic_CommandLineParsing.cs b/grate.unittests/Basic/CommandLineParsing/Basic_CommandLineParsing.cs index f3a252cd..1ba40bf4 100644 --- a/grate.unittests/Basic/CommandLineParsing/Basic_CommandLineParsing.cs +++ b/grate.unittests/Basic/CommandLineParsing/Basic_CommandLineParsing.cs @@ -8,6 +8,7 @@ using grate.Commands; using grate.Configuration; using grate.Infrastructure; +using grate.unittests.TestInfrastructure; using NUnit.Framework; namespace grate.unittests.Basic.CommandLineParsing; @@ -65,6 +66,21 @@ public async Task AdminConnectionString(string argName) cfg?.AdminConnectionString.Should().Be(database); } + [TestCase(DatabaseType.mariadb)] + [TestCase(DatabaseType.oracle)] + [TestCase(DatabaseType.postgresql)] + [TestCase(DatabaseType.sqlite)] + [TestCase(DatabaseType.sqlserver)] + public async Task DefaultAdminConnectionString(DatabaseType databaseType) + { + var commandline = $"--connectionstring=;Database=jalla --databasetype={databaseType}"; + var cfg = await ParseGrateConfiguration(commandline); + + var masterDbName = TestConfig.GetTestContext(databaseType).MasterDatabase; + + cfg?.AdminConnectionString.Should().Be($";Database="+masterDbName); + } + [TestCase("-f ")] [TestCase("--files=")] [TestCase("--sqlfilesdirectory=")] diff --git a/grate.unittests/TestInfrastructure/OracleGrateTestContext.cs b/grate.unittests/TestInfrastructure/OracleGrateTestContext.cs index 3f24c868..064f72c0 100644 --- a/grate.unittests/TestInfrastructure/OracleGrateTestContext.cs +++ b/grate.unittests/TestInfrastructure/OracleGrateTestContext.cs @@ -29,6 +29,7 @@ public string DockerCommand(string serverName, string adminPassword) => public DatabaseType DatabaseType => DatabaseType.oracle; public bool SupportsTransaction => false; + public string DatabaseTypeName => "Oracle"; public string MasterDatabase => "oracle"; diff --git a/grate.unittests/TestInfrastructure/TestConfig.cs b/grate.unittests/TestInfrastructure/TestConfig.cs index c7b76cc1..efa3aaba 100644 --- a/grate.unittests/TestInfrastructure/TestConfig.cs +++ b/grate.unittests/TestInfrastructure/TestConfig.cs @@ -1,6 +1,7 @@ using System; using System.IO; using System.Linq; +using grate.Configuration; using Microsoft.Extensions.Logging; using static System.StringSplitOptions; @@ -67,5 +68,15 @@ public static DirectoryInfo MakeSurePathExists(DirectoryInfo? path) return path; } - + + public static IGrateTestContext GetTestContext(DatabaseType databaseType) => databaseType switch + { + DatabaseType.mariadb => new MariaDbGrateTestContext(), + DatabaseType.oracle => new OracleGrateTestContext(), + DatabaseType.postgresql => new PostgreSqlGrateTestContext(), + DatabaseType.sqlite => new SqliteGrateTestContext(), + DatabaseType.sqlserver => new SqlServerGrateTestContext(), + _ => throw new ArgumentOutOfRangeException(nameof(databaseType), databaseType.ToString()) + }; + } diff --git a/grate/Configuration/GrateConfiguration.cs b/grate/Configuration/GrateConfiguration.cs index fa049038..c96d5a0a 100644 --- a/grate/Configuration/GrateConfiguration.cs +++ b/grate/Configuration/GrateConfiguration.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.IO; using System.Text.RegularExpressions; using grate.Infrastructure; @@ -34,14 +35,15 @@ public string? AdminConnectionString public string? AccessToken { get; set; } = null; - private static string? WithAdminDb(string? connectionString) + private string? WithAdminDb(string? connectionString) { if (string.IsNullOrEmpty(connectionString)) { return connectionString; } var pattern = new Regex("(.*;\\s*(?:Initial Catalog|Database)=)([^;]*)(.*)"); - var replaced = pattern.Replace(connectionString, "$1master$3"); + var replacement = $"$1{GetMasterDbName(DatabaseType)}$3"; + var replaced = pattern.Replace(connectionString, replacement); return replaced; } @@ -119,4 +121,15 @@ public string? AdminConnectionString /// If specified, location of the backup file to use when restoring /// public string? Restore { get; init; } + + private static string GetMasterDbName(DatabaseType databaseType) => databaseType switch + { + DatabaseType.mariadb => "mysql", + DatabaseType.oracle => "oracle", + DatabaseType.postgresql => "postgres", + DatabaseType.sqlite => "master", + DatabaseType.sqlserver => "master", + _ => throw new ArgumentOutOfRangeException(nameof(databaseType), databaseType.ToString()) + }; + } From 4fb56f58774b244184e5312ce537c48c7e64377f Mon Sep 17 00:00:00 2001 From: Matt Gallagher <46973220+mattgallagher92@users.noreply.github.com> Date: Tue, 4 Apr 2023 19:41:11 +0100 Subject: [PATCH 46/63] Update configuration docs to reflect pull #314 (#318) --- docs/ConfigurationOptions/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/ConfigurationOptions/index.md b/docs/ConfigurationOptions/index.md index eda9bb1f..ae46e7d0 100644 --- a/docs/ConfigurationOptions/index.md +++ b/docs/ConfigurationOptions/index.md @@ -21,7 +21,7 @@ grate --connectionstring="Server=(localdb)\MSSQLLocalDB;Integrated Security=true | Option | Default | Purpose | | ------ | ------- | ------- | | -c
-cs
--connectionstring
--connstring <connectionstring> | - | **REQUIRED** You now provide an entire connection string. ServerName and Database are obsolete. | -| -a
-acs
-csa
--adminconnectionstring
--adminconnstring <adminconnectionstring> | The value provided via --connectionstring, with the target database replaced with "master" | Used when creating a new database, rather than migrating an existing one. Must be set manually when creating new databases on DBMSs where there isn't guaranteed to be a database called "master". | +| -a
-acs
-csa
--adminconnectionstring
--adminconnstring <adminconnectionstring> | The value provided via --connectionstring, with the target database replaced with a database that can be assumed to be present. For example, "master" for SQL Server. | Used when creating a new database, rather than migrating an existing one. | | -f
--files
--sqlfilesdirectory <sqlfilesdirectory> | . (current directory) | The directory where your SQL scripts are located | | --folders | Default folders as described in [Getting started](../GettingStarted.md) | Folder configuration, see [Folder configuration](FolderConfiguration.md) for details. | | -o
--output
--outputPath <outputPath> | %LOCALAPPDATA%/grate | This is where everything related to the migration is stored. This includes any backups, all items that ran, permission dumps, logs, etc. | From 165451744fb4cc37b22b973e752f759eec8e852c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Apr 2023 20:55:22 +0200 Subject: [PATCH 47/63] build(deps): bump Microsoft.Data.SqlClient from 5.1.0 to 5.1.1 (#320) * build(deps): bump Microsoft.Data.SqlClient from 5.1.0 to 5.1.1 Bumps [Microsoft.Data.SqlClient](https://github.com/dotnet/sqlclient) from 5.1.0 to 5.1.1. - [Release notes](https://github.com/dotnet/sqlclient/releases) - [Changelog](https://github.com/dotnet/SqlClient/blob/main/CHANGELOG.md) - [Commits](https://github.com/dotnet/sqlclient/compare/v5.1.0...v5.1.1) --- updated-dependencies: - dependency-name: Microsoft.Data.SqlClient dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * Update grate.unittests.csproj 5.1.1 -> 5.1.* --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Erik A. Brandstadmoen --- grate.unittests/grate.unittests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grate.unittests/grate.unittests.csproj b/grate.unittests/grate.unittests.csproj index 84d4e1e6..41d60b7e 100644 --- a/grate.unittests/grate.unittests.csproj +++ b/grate.unittests/grate.unittests.csproj @@ -14,7 +14,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all
- + From a51537edac08b0ac2237c31df33cb77ea7cc3931 Mon Sep 17 00:00:00 2001 From: "Erik A. Brandstadmoen" Date: Thu, 6 Apr 2023 00:35:37 +0200 Subject: [PATCH 48/63] Bug #232: Disable connection pooling by default for SqlServer (#322) * Bug #232: Disable connection pooling by default for SqlServer Microsoft.Data.SqlClient v 5.x has connection pooling enabled by default. This can create issues for grate due to that we expect connections to actually be closed when we close them. If they aren't (which they aren't when connection pooling is enabled), some scripts, that alter the state of the database, might fail. --- .../SqlServer/SqlServerDatabase_.cs | 50 +++++++++++++++++++ grate.unittests/SqlServer/Database.cs | 1 - grate/Migration/SqlServerDatabase.cs | 9 ++++ 3 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 grate.unittests/Basic/Infrastructure/SqlServer/SqlServerDatabase_.cs diff --git a/grate.unittests/Basic/Infrastructure/SqlServer/SqlServerDatabase_.cs b/grate.unittests/Basic/Infrastructure/SqlServer/SqlServerDatabase_.cs new file mode 100644 index 00000000..52df01df --- /dev/null +++ b/grate.unittests/Basic/Infrastructure/SqlServer/SqlServerDatabase_.cs @@ -0,0 +1,50 @@ +using System.Data.Common; +using System.Threading.Tasks; +using FluentAssertions; +using grate.Configuration; +using grate.Migration; +using grate.unittests.TestInfrastructure; +using Microsoft.Data.SqlClient; +using Microsoft.Extensions.Logging; +using NUnit.Framework; + +namespace grate.unittests.Basic.Infrastructure.SqlServer; + +// ReSharper disable once InconsistentNaming +public class SqlServerDatabase_ +{ + [Test] + public async Task Disables_pooling_if_not_explicitly_set_in_connection_string() + { + var connStr = "Server=dummy"; + var cfg = new GrateConfiguration() { ConnectionString = connStr }; + var sqlServerDatabase = new InspectableSqlServerDatabase(); + await sqlServerDatabase.InitializeConnections(cfg); + + var conn = sqlServerDatabase.GetConnection(); + var builder = new SqlConnectionStringBuilder(conn.ConnectionString); + builder.Pooling.Should().BeFalse(); + } + + [Test] + public async Task Leaves_pooling_as_configured_if_set_explicitly_in_connection_string() + { + var connStr = "Server=dummy;Pooling=true"; + var cfg = new GrateConfiguration() { ConnectionString = connStr }; + var sqlServerDatabase = new InspectableSqlServerDatabase(); + await sqlServerDatabase.InitializeConnections(cfg); + + var conn = sqlServerDatabase.GetConnection(); + var builder = new SqlConnectionStringBuilder(conn.ConnectionString); + builder.Pooling.Should().BeTrue(); + } + + private class InspectableSqlServerDatabase : SqlServerDatabase + { + public InspectableSqlServerDatabase() : base(TestConfig.LogFactory.CreateLogger()) + { + } + + public DbConnection GetConnection() => base.Connection; + } +} diff --git a/grate.unittests/SqlServer/Database.cs b/grate.unittests/SqlServer/Database.cs index 7fde4f01..8669fd84 100644 --- a/grate.unittests/SqlServer/Database.cs +++ b/grate.unittests/SqlServer/Database.cs @@ -29,5 +29,4 @@ public async Task Does_not_needlessly_apply_case_sensitive_database_name_checks_ // There should be no errors running the migration Assert.DoesNotThrowAsync(() => migrator.Migrate()); } - } diff --git a/grate/Migration/SqlServerDatabase.cs b/grate/Migration/SqlServerDatabase.cs index 68e33a44..51c39f6a 100644 --- a/grate/Migration/SqlServerDatabase.cs +++ b/grate/Migration/SqlServerDatabase.cs @@ -19,6 +19,15 @@ public SqlServerDatabase(ILogger logger) protected override bool SupportsSchemas => true; protected override DbConnection GetSqlConnection(string? connectionString) { + // If pooling is not explicitly mentioned in the connection string, turn it off, as enabling it + // might lead to problems in more scenarios than it (potentially) solves, in the most + // common grate scenarios. + if (!(connectionString ?? "").Contains("Pooling", StringComparison.InvariantCultureIgnoreCase)) + { + var builder = new SqlConnectionStringBuilder(connectionString) { Pooling = false }; + connectionString = builder.ConnectionString; + } + var conn = new SqlConnection(connectionString); conn.AccessToken = AccessToken; From 689459c1f5cb224b34c1a5d34f89d3598162c3dc Mon Sep 17 00:00:00 2001 From: "Erik A. Brandstadmoen" Date: Thu, 6 Apr 2023 01:20:25 +0200 Subject: [PATCH 49/63] Feat/252 custom create database (#289) Allow for custom CREATE DATABASE and DROP DATABASE scripts in folders, similar to up, beforeMigration, afterMigration, etc. If you have the need for any custom create database logic, put it in this folder, and it will be used instead of the standard. --- .../FolderConfiguration.md | 2 + .../KnownFolders_CustomNames.cs | 1 + .../KnownFolders_Default.cs | 24 +++---- grate.unittests/Generic/GenericDatabase.cs | 40 +++++++++++ grate.unittests/SqLite/Database.cs | 4 ++ grate/Configuration/FoldersConfiguration.cs | 14 ++-- grate/Configuration/IFoldersConfiguration.cs | 2 + grate/Configuration/IKnownFolderNames.cs | 2 + grate/Configuration/KnownFolderKeys.cs | 1 + grate/Configuration/KnownFolderNames.cs | 2 + grate/Migration/DbMigrator.cs | 64 +++++++++++++++++ grate/Migration/GrateMigrator.cs | 72 ++++++++++++++++++- grate/Migration/IDbMigrator.cs | 5 ++ grate/Migration/MariaDbDatabase.cs | 4 +- 14 files changed, 217 insertions(+), 20 deletions(-) diff --git a/docs/ConfigurationOptions/FolderConfiguration.md b/docs/ConfigurationOptions/FolderConfiguration.md index cb911578..07f0644d 100644 --- a/docs/ConfigurationOptions/FolderConfiguration.md +++ b/docs/ConfigurationOptions/FolderConfiguration.md @@ -131,6 +131,8 @@ grate processes the files in a standard set of directories in a fixed order for | Folder | Script type | Explanation | | ------ | ------- |------- | +| (-1. dropDatabase) | Anytime scripts | If you have the need for a custom `DROP DATABASE` script (used with the `--drop` command-line flag) | +| (0. createDatabase) | Anytime scripts | If you have the need for a custom `CREATE DATABASE` script, put it here, and it will be used instead of the default. | | 1. beforeMigration | Everytime scripts | If you have particular tasks you want to perform prior to any database migrations (custom logging? database backups? disable replication?) you can do it here. | | 2. alterDatabase | Anytime scripts | If you have scripts that need to alter the database config itself (rather than the _contents_ of the database) thjis is the place to do it. For example setting recovery modes, enabling query stores, etc etc | | 3. runAfterCreateDatabase | Anytime scripts | This directory is only processed if the database was created from scratch by grate. Maybe you need to add user accounts or similar? diff --git a/grate.unittests/Basic/Infrastructure/FolderConfiguration/KnownFolders_CustomNames.cs b/grate.unittests/Basic/Infrastructure/FolderConfiguration/KnownFolders_CustomNames.cs index d46f813d..8292a7dd 100644 --- a/grate.unittests/Basic/Infrastructure/FolderConfiguration/KnownFolders_CustomNames.cs +++ b/grate.unittests/Basic/Infrastructure/FolderConfiguration/KnownFolders_CustomNames.cs @@ -69,6 +69,7 @@ TransactionHandling transactionHandling private static readonly IKnownFolderNames OverriddenFolderNames = new KnownFolderNames() { BeforeMigration = "beforeMigration" + Random.GetString(8), + CreateDatabase = "createDatabase" + Random.GetString(8), AlterDatabase = "alterDatabase" + Random.GetString(8), RunAfterCreateDatabase = "runAfterCreateDatabase" + Random.GetString(8), RunBeforeUp = "runBeforeUp" + Random.GetString(8), diff --git a/grate.unittests/Basic/Infrastructure/FolderConfiguration/KnownFolders_Default.cs b/grate.unittests/Basic/Infrastructure/FolderConfiguration/KnownFolders_Default.cs index b1399d31..1140e150 100644 --- a/grate.unittests/Basic/Infrastructure/FolderConfiguration/KnownFolders_Default.cs +++ b/grate.unittests/Basic/Infrastructure/FolderConfiguration/KnownFolders_Default.cs @@ -45,12 +45,12 @@ public void Returns_folders_in_current_order() [Test] [TestCaseSource(nameof(ExpectedKnownFolderNames))] public void Has_expected_folder_configuration( - MigrationsFolder folder, - string expectedName, - MigrationType expectedType, - ConnectionType expectedConnectionType, - TransactionHandling transactionHandling - ) + MigrationsFolder folder, + string expectedName, + MigrationType expectedType, + ConnectionType expectedConnectionType, + TransactionHandling transactionHandling + ) { var root = Root.ToString(); @@ -94,11 +94,11 @@ private static TestCaseData GetTestCase( ) => new TestCaseData(folder, expectedName, expectedType, expectedConnectionType, transactionHandling) .SetArgDisplayNames( - migrationsFolderDefinitionName, - expectedName, - expectedType.ToString(), - "conn: " + expectedConnectionType, - "tran: " + transactionHandling - ); + migrationsFolderDefinitionName, + expectedName, + expectedType.ToString(), + "conn: " + expectedConnectionType, + "tran: " + transactionHandling + ); } diff --git a/grate.unittests/Generic/GenericDatabase.cs b/grate.unittests/Generic/GenericDatabase.cs index 9e1114fa..aa6586b9 100644 --- a/grate.unittests/Generic/GenericDatabase.cs +++ b/grate.unittests/Generic/GenericDatabase.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Data.Common; +using System.IO; using System.Linq; using System.Threading.Tasks; using System.Transactions; @@ -8,7 +9,9 @@ using grate.Configuration; using grate.Migration; using grate.unittests.TestInfrastructure; +using Microsoft.Data.SqlClient; using NUnit.Framework; +using static System.StringSplitOptions; namespace grate.unittests.Generic; @@ -28,6 +31,39 @@ public async Task Is_created_if_confed_and_it_does_not_exist() IEnumerable databases = await GetDatabases(); databases.Should().Contain(db); } + + [Test] + public virtual async Task Is_created_with_custom_script_if_custom_create_database_folder_exists() + { + var scriptedDatabase = "CUSTOMSCRIPTEDDATABASE"; + var confedDatabase = "DEFAULTDATABASE"; + + var config = GetConfiguration(confedDatabase, true); + var password = Context.AdminConnectionString + .Split(";", TrimEntries | RemoveEmptyEntries) + .SingleOrDefault(entry => entry.StartsWith("Password") || entry.StartsWith("Pwd"))? + .Split("=", TrimEntries | RemoveEmptyEntries) + .Last(); + + var customScript = Context.Syntax.CreateDatabase(scriptedDatabase, password); + TestConfig.WriteContent(Wrap(config.SqlFilesDirectory, config.Folders?.CreateDatabase?.Path), "createDatabase.sql", customScript); + try + { + await using var migrator = GetMigrator(config); + await migrator.Migrate(); + } + catch (DbException) + { + //Do nothing because database name is wrong due to custom script + } + + File.Delete(Path.Join(Wrap(config.SqlFilesDirectory, config.Folders?.CreateDatabase?.Path).ToString(), "createDatabase.sql")); + + // The database should have been created by the custom script + IEnumerable databases = (await GetDatabases()).ToList(); + databases.Should().Contain(scriptedDatabase); + databases.Should().NotContain(confedDatabase); + } [Test] public async Task Is_not_created_if_not_confed() @@ -180,4 +216,8 @@ private GrateConfiguration GetConfiguration(bool createDatabase, string? connect }; } + + protected static DirectoryInfo Wrap(DirectoryInfo root, string? subFolder) => + new DirectoryInfo(Path.Combine(root.ToString(), subFolder ?? "")); + } diff --git a/grate.unittests/SqLite/Database.cs b/grate.unittests/SqLite/Database.cs index 8c7272c1..4e4e70b6 100644 --- a/grate.unittests/SqLite/Database.cs +++ b/grate.unittests/SqLite/Database.cs @@ -42,5 +42,9 @@ protected override async Task> GetDatabases() return await ValueTask.FromResult(dbNames); } + [Ignore("SQLite does not support custom database creation script")] + public override Task Is_created_with_custom_script_if_custom_create_database_folder_exists() => + Task.CompletedTask; + protected override bool ThrowOnMissingDatabase => false; } diff --git a/grate/Configuration/FoldersConfiguration.cs b/grate/Configuration/FoldersConfiguration.cs index 8529d957..d6729719 100644 --- a/grate/Configuration/FoldersConfiguration.cs +++ b/grate/Configuration/FoldersConfiguration.cs @@ -23,15 +23,17 @@ public FoldersConfiguration(IDictionary source) public FoldersConfiguration() { } - + + public MigrationsFolder? CreateDatabase { get; set; } + public MigrationsFolder? DropDatabase { get; set; } public static FoldersConfiguration Empty => new(); public static IFoldersConfiguration Default(IKnownFolderNames? folderNames = null) { folderNames ??= KnownFolderNames.Default; - - return new FoldersConfiguration() + + var foldersConfiguration = new FoldersConfiguration() { { KnownFolderKeys.BeforeMigration, new MigrationsFolder("BeforeMigration", folderNames.BeforeMigration, EveryTime, TransactionHandling: TransactionHandling.Autonomous) }, { KnownFolderKeys.AlterDatabase , new MigrationsFolder("AlterDatabase", folderNames.AlterDatabase, AnyTime, ConnectionType.Admin, TransactionHandling.Autonomous) }, @@ -46,8 +48,12 @@ public static IFoldersConfiguration Default(IKnownFolderNames? folderNames = nul { KnownFolderKeys.Indexes, new MigrationsFolder("Indexes", folderNames.Indexes, AnyTime) }, { KnownFolderKeys.RunAfterOtherAnyTimeScripts, new MigrationsFolder("Run after Other Anytime Scripts", folderNames.RunAfterOtherAnyTimeScripts, AnyTime) }, { KnownFolderKeys.Permissions, new MigrationsFolder("Permissions", folderNames.Permissions, EveryTime, TransactionHandling: TransactionHandling.Autonomous) }, - { KnownFolderKeys.AfterMigration, new MigrationsFolder("AfterMigration", folderNames.AfterMigration, EveryTime, TransactionHandling: TransactionHandling.Autonomous) } + { KnownFolderKeys.AfterMigration, new MigrationsFolder("AfterMigration", folderNames.AfterMigration, EveryTime, TransactionHandling: TransactionHandling.Autonomous) }, }; + foldersConfiguration.CreateDatabase = new MigrationsFolder("CreateDatabase", folderNames.CreateDatabase, AnyTime, ConnectionType.Admin, TransactionHandling.Autonomous); + foldersConfiguration.DropDatabase = new MigrationsFolder("DropDatabase", folderNames.DropDatabase, AnyTime, ConnectionType.Admin, TransactionHandling.Autonomous); + + return foldersConfiguration; } } diff --git a/grate/Configuration/IFoldersConfiguration.cs b/grate/Configuration/IFoldersConfiguration.cs index 7e88e0c9..4fda0bc4 100644 --- a/grate/Configuration/IFoldersConfiguration.cs +++ b/grate/Configuration/IFoldersConfiguration.cs @@ -4,4 +4,6 @@ namespace grate.Configuration; public interface IFoldersConfiguration: IDictionary { + MigrationsFolder? CreateDatabase { get; set; } + MigrationsFolder? DropDatabase { get; set; } } diff --git a/grate/Configuration/IKnownFolderNames.cs b/grate/Configuration/IKnownFolderNames.cs index c4fe97d0..dc67d7d9 100644 --- a/grate/Configuration/IKnownFolderNames.cs +++ b/grate/Configuration/IKnownFolderNames.cs @@ -3,6 +3,8 @@ public interface IKnownFolderNames { string BeforeMigration { get; } + string CreateDatabase { get; } + string DropDatabase { get; } string AlterDatabase { get; } string RunAfterCreateDatabase { get; } string RunBeforeUp { get; } diff --git a/grate/Configuration/KnownFolderKeys.cs b/grate/Configuration/KnownFolderKeys.cs index d227c6cb..58def349 100644 --- a/grate/Configuration/KnownFolderKeys.cs +++ b/grate/Configuration/KnownFolderKeys.cs @@ -5,6 +5,7 @@ namespace grate.Configuration; public static class KnownFolderKeys { public const string BeforeMigration = nameof(BeforeMigration); + public const string CreateDatabase = nameof(CreateDatabase); public const string AlterDatabase = nameof(AlterDatabase); public const string RunAfterCreateDatabase = nameof(RunAfterCreateDatabase); public const string RunBeforeUp = nameof(RunBeforeUp); diff --git a/grate/Configuration/KnownFolderNames.cs b/grate/Configuration/KnownFolderNames.cs index 507fafea..b05a1c4e 100644 --- a/grate/Configuration/KnownFolderNames.cs +++ b/grate/Configuration/KnownFolderNames.cs @@ -3,6 +3,8 @@ public record KnownFolderNames: IKnownFolderNames { public string BeforeMigration { get; init; } = "beforeMigration"; + public string CreateDatabase { get; init; } = "createDatabase"; + public string DropDatabase { get; init; } = "dropDatabase"; public string AlterDatabase { get; init; } = "alterDatabase"; public string RunAfterCreateDatabase { get; init; } = "runAfterCreateDatabase"; public string RunBeforeUp { get; init; } = "runBeforeUp"; diff --git a/grate/Migration/DbMigrator.cs b/grate/Migration/DbMigrator.cs index a48df486..aa343efe 100644 --- a/grate/Migration/DbMigrator.cs +++ b/grate/Migration/DbMigrator.cs @@ -154,6 +154,42 @@ async Task LogAndRunSql() return theSqlWasRun; } + + public async Task RunSqlWithoutLogging( + string sql, + string scriptName, + GrateEnvironment? environment, + ConnectionType connectionType, + TransactionHandling transactionHandling) + { + async Task PrintLogAndRunSql() + { + _logger.LogInformation(" Running '{ScriptName}'.", scriptName); + + if (Configuration.DryRun) + { + return false; + } + else + { + await RunTheActualSqlWithoutLogging(sql, scriptName, connectionType, transactionHandling); + return true; + } + } + + if (!InCorrectEnvironment(scriptName, environment)) + { + return false; + } + + if (TokenReplacementEnabled) + { + sql = ReplaceTokensIn(sql); + } + + return await PrintLogAndRunSql();; + } + public async Task RestoreDatabase(string backupPath) { @@ -273,6 +309,34 @@ private async Task RunTheActualSql(string sql, await RecordScriptInScriptsRunTable(scriptName, sql, migrationType, versionId, transactionHandling); } + + private async Task RunTheActualSqlWithoutLogging( + string sql, + string scriptName, + ConnectionType connectionType, + TransactionHandling transactionHandling) + { + foreach (var statement in GetStatements(sql)) + { + try + { + await Database.RunSql(statement, connectionType, transactionHandling); + } + catch (Exception ex) + { + _logger.LogError("Error running script \"{ScriptName}\": {ErrorMessage}", scriptName, ex.Message); + + if (Transaction.Current is not null) { + Database.Rollback(); + } + + await Database.CloseConnection(); + Transaction.Current?.Dispose(); + throw; + } + } + } + private IEnumerable GetStatements(string sql) => StatementSplitter.Split(sql); diff --git a/grate/Migration/GrateMigrator.cs b/grate/Migration/GrateMigrator.cs index b1e35f44..baf31df3 100644 --- a/grate/Migration/GrateMigrator.cs +++ b/grate/Migration/GrateMigrator.cs @@ -229,7 +229,7 @@ private async Task CreateGrateStructure(IDbMigrator dbMigrator) return (versionId, newVersion); } - private static async Task CreateDatabaseIfItDoesNotExist(IDbMigrator dbMigrator) + private async Task CreateDatabaseIfItDoesNotExist(IDbMigrator dbMigrator) { bool databaseCreated; if (await dbMigrator.DatabaseExists()) @@ -238,7 +238,28 @@ private static async Task CreateDatabaseIfItDoesNotExist(IDbMigrator dbMig } else { - databaseCreated = await dbMigrator.CreateDatabase(); + var config = dbMigrator.Configuration; + var createDatabaseFolder = config.Folders?.CreateDatabase; + var database = _migrator.Database; + + var path = Wrap(config.SqlFilesDirectory, createDatabaseFolder?.Path ?? "zz-xx-øø-definitely-does-not-exist"); + + if (createDatabaseFolder is not null && path.Exists) + { + //await LogAndProcess(config.SqlFilesDirectory, folder!, changeDropFolder, versionId, folder!.ConnectionType, folder.TransactionHandling); + var changeDropFolder = ChangeDropFolder(config, database.ServerName, database.DatabaseName); + databaseCreated = await ProcessWithoutLogging( + config.SqlFilesDirectory, + createDatabaseFolder, + changeDropFolder, + createDatabaseFolder.ConnectionType, + createDatabaseFolder.TransactionHandling + ); + } + else + { + databaseCreated = await dbMigrator.CreateDatabase(); + } } return databaseCreated; } @@ -330,6 +351,53 @@ private async Task Process(DirectoryInfo root, MigrationsFolder folder, string c } } + + private async Task ProcessWithoutLogging(DirectoryInfo root, MigrationsFolder folder, string changeDropFolder, + ConnectionType connectionType, TransactionHandling transactionHandling) + { + var path = Wrap(root, folder.Path); + + await EnsureConnectionIsOpen(connectionType); + + var pattern = "*.sql"; + var files = FileSystem.GetFiles(path, pattern); + + var anySqlRun = false; + + foreach (var file in files) + { + var sql = await File.ReadAllTextAsync(file.FullName); + + // Normalize file names to log, so that results won't vary if you run on *nix VS Windows + var fileNameToLog = string.Join('/', + Path.GetRelativePath(path.ToString(), file.FullName).Split(Path.DirectorySeparatorChar)); + + bool theSqlRan = await _migrator.RunSqlWithoutLogging(sql, fileNameToLog, _migrator.Configuration.Environment, + connectionType, transactionHandling); + + if (theSqlRan) + { + anySqlRun = true; + try + { + CopyToChangeDropFolder(path.Parent!, file, changeDropFolder); + } + catch (Exception ex) + { + _logger.LogWarning(ex, "Unable to copy {File} to {ChangeDropFolder}. \n{Exception}", file, changeDropFolder, ex.Message); + } + } + } + + if (!anySqlRun) + { + _logger.LogInformation(" No sql run, either an empty folder, or all files run against destination previously."); + } + + return anySqlRun; + + } + private void CopyToChangeDropFolder(DirectoryInfo migrationRoot, FileSystemInfo file, string changeDropFolder) { diff --git a/grate/Migration/IDbMigrator.cs b/grate/Migration/IDbMigrator.cs index 2ee074a2..6cb60fac 100644 --- a/grate/Migration/IDbMigrator.cs +++ b/grate/Migration/IDbMigrator.cs @@ -26,10 +26,15 @@ public interface IDbMigrator: IAsyncDisposable Task VersionTheDatabase(string newVersion); Task OpenAdminConnection(); Task CloseAdminConnection(); + Task RunSql(string sql, string scriptName, MigrationType migrationType, long versionId, GrateEnvironment? environment, ConnectionType connectionType, TransactionHandling transactionHandling); + Task RunSqlWithoutLogging(string sql, string scriptName, + GrateEnvironment? environment, + ConnectionType connectionType, TransactionHandling transactionHandling); + Task RestoreDatabase(string backupPath); void SetDefaultConnectionActive(); Task OpenNewActiveConnection(); diff --git a/grate/Migration/MariaDbDatabase.cs b/grate/Migration/MariaDbDatabase.cs index 9091573a..527e2e06 100644 --- a/grate/Migration/MariaDbDatabase.cs +++ b/grate/Migration/MariaDbDatabase.cs @@ -30,7 +30,7 @@ public override async Task DropDatabase() // We need to kill any active connections to get MariaDB to actually delete the database, // and stop accepting new connections to it. So we create a list of the - // active sessions against our databse, and create 'KILL X' statements (where X is session id). + // active sessions against our database, and create 'KILL X' statements (where X is session id). // Then we execute the kill statements. var sql = $@" SELECT GROUP_CONCAT(CONCAT('KILL ',id,';') SEPARATOR ' ') @@ -39,7 +39,7 @@ public override async Task DropDatabase() var killStatements = await ExecuteScalarAsync(AdminConnection, sql); if (killStatements != null && !DBNull.Value.Equals(killStatements)) { - string killSql = killStatements.ToString() ?? ""; // Just to keel warnings happy + string killSql = killStatements.ToString() ?? ""; // Just to keep warnings happy await ExecuteNonQuery(AdminConnection, killSql, null); } From e1aa4043460f4a0c381e440faffa3fbadcf429f1 Mon Sep 17 00:00:00 2001 From: "Erik A. Brandstadmoen" Date: Thu, 6 Apr 2023 02:24:19 +0200 Subject: [PATCH 50/63] Feat #61: Added doc on how to install with homebrew (#323) * Added doc on how to install with homebrew --- docs/GettingGrate.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/GettingGrate.md b/docs/GettingGrate.md index 54e01229..31fbc3ca 100644 --- a/docs/GettingGrate.md +++ b/docs/GettingGrate.md @@ -41,6 +41,10 @@ please install [dotnet 6](https://dotnet.microsoft.com/download/dotnet/6.0) grate is available on [winget](https://docs.microsoft.com/en-us/windows/package-manager/winget/). Simply `winget install erikbra.grate` for awesome! +## Homebrew + +grate is available as a Homebrew cask. Simply `brew install --cask erikbra/cask/grate` for awesomeness! + ## Notes Plans are afoot for more OS specific package managers, watch this space. From 7da5d9aa880f17be5e922118e7ff174df496e29f Mon Sep 17 00:00:00 2001 From: "Erik A. Brandstadmoen" Date: Thu, 6 Apr 2023 12:14:43 +0200 Subject: [PATCH 51/63] Disable creation of winget package on deploy - it has dependencies on published artifacts (which are not published yet) --- .github/workflows/build.yml | 50 ++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 305c9663..c73e4b25 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -224,31 +224,31 @@ jobs: name: grate_${{ needs.set-version-number.outputs.nuGetVersion }}-1_${{ steps.get-arch.outputs.arch}}.deb path: ./installers/deb/grate_${{ needs.set-version-number.outputs.nuGetVersion }}-1_${{ steps.get-arch.outputs.arch }}.deb - build-winget: - name: Winget - Update package manifest in the OWC - needs: - - set-version-number - - build-msi - runs-on: windows-latest - if: ${{ needs.set-version-number.outputs.is-release == 'true' }} - - steps: - - name: Winget-Create - run: | - - $version = "$($env:version)" - - # Download wingetcreate - iwr https://aka.ms/wingetcreate/latest -OutFile wingetcreate.exe - - $packageUrl="https://github.com/erikbra/grate/releases/download/$version/grate-$version.msi" - - echo "Running ./wingetcreate.exe update erikbra.grate -u $packageUrl -v $version -t `"$env:WINGET_GH_PAT`" --submit" - ./wingetcreate.exe update erikbra.grate -u $packageUrl -v $version -t "$env:WINGET_GH_PAT" --submit - env: - WINGET_GH_PAT: ${{ secrets.WINGET_GH_PAT }} - #version: "1.4.0" - version: "${{ needs.set-version-number.outputs.nuGetVersion }}" + # build-winget: + # name: Winget - Update package manifest in the OWC + # needs: + # - set-version-number + # - build-msi + # runs-on: windows-latest + # if: ${{ needs.set-version-number.outputs.is-release == 'true' }} + + # steps: + # - name: Winget-Create + # run: | + + # $version = "$($env:version)" + + # # Download wingetcreate + # iwr https://aka.ms/wingetcreate/latest -OutFile wingetcreate.exe + + # $packageUrl="https://github.com/erikbra/grate/releases/download/$version/grate-$version.msi" + + # echo "Running ./wingetcreate.exe update erikbra.grate -u $packageUrl -v $version -t `"$env:WINGET_GH_PAT`" --submit" + # ./wingetcreate.exe update erikbra.grate -u $packageUrl -v $version -t "$env:WINGET_GH_PAT" --submit + # env: + # WINGET_GH_PAT: ${{ secrets.WINGET_GH_PAT }} + # #version: "1.4.0" + # version: "${{ needs.set-version-number.outputs.nuGetVersion }}" test: From 7c3ac865e4b7faac058b1e04953a46389d81de76 Mon Sep 17 00:00:00 2001 From: "Erik A. Brandstadmoen" Date: Mon, 10 Apr 2023 00:32:45 +0200 Subject: [PATCH 52/63] Bug #247: Don't trim assemblies on Windows - SNI problems (#326) There are some issues with SNI dlls (native C++ DLL used only on Windows when connecting to SQL server) when trimming assemblies. So, avoid trimming on Windows. It makes the binary 40MB instead of 20MB, but, nevermind, that's a minor issue nowadays. See https://learn.microsoft.com/en-us/sql/connect/ado-net/sqlclient-troubleshooting-guide?view=sql-server-ver16#issues-in-net-core-applications Solves #247 --- grate/grate.csproj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/grate/grate.csproj b/grate/grate.csproj index 7b9318cd..1587f984 100644 --- a/grate/grate.csproj +++ b/grate/grate.csproj @@ -56,4 +56,8 @@ up using modern .NET 6/7. true + + false + + From 62c22e3bd28c51a1c47ce85cd74beba8946e9ab3 Mon Sep 17 00:00:00 2001 From: "Erik A. Brandstadmoen" Date: Mon, 10 Apr 2023 20:31:40 +0200 Subject: [PATCH 53/63] Bug #298: Error inserting large scripts in ScriptsRun and -Errors tables on Oracle (#327) Original fix by @ryanmwright in PR #324. Resolves #298 * Wrote failing unit test demonstrating issue * Ported the necessary parts of PR #324 to make tests pass --- .../Failing_Scripts.cs | 46 ++++++++++++++++++- .../MigrationsScriptsBase.cs | 38 ++++++++++++++- .../ScriptsRun_Table.cs | 34 ++++++++++++-- .../TestInfrastructure/SqlStatements.cs | 1 + grate/Migration/AnsiSqlDatabase.cs | 39 ++++++++-------- 5 files changed, 133 insertions(+), 25 deletions(-) diff --git a/grate.unittests/Generic/Running_MigrationScripts/Failing_Scripts.cs b/grate.unittests/Generic/Running_MigrationScripts/Failing_Scripts.cs index e977b25e..ba8cafb5 100644 --- a/grate.unittests/Generic/Running_MigrationScripts/Failing_Scripts.cs +++ b/grate.unittests/Generic/Running_MigrationScripts/Failing_Scripts.cs @@ -1,4 +1,5 @@ -using System.Data.Common; +using System; +using System.Data.Common; using System.IO; using System.Linq; using System.Runtime.CompilerServices; @@ -73,6 +74,42 @@ public async Task Inserts_Failed_Scripts_Into_ScriptRunErrors_Table() scripts.Should().HaveCount(1); } + [Test()] + public async Task Inserts_Large_Failed_Scripts_Into_ScriptRunErrors_Table() + { + var db = TestConfig.RandomDatabase(); + + var parent = TestConfig.CreateRandomTempDirectory(); + var knownFolders = FoldersConfiguration.Default(null); + GrateMigrator? migrator; + + CreateLongInvalidSql(parent, knownFolders[Up]); + + await using (migrator = Context.GetMigrator(db, parent, knownFolders)) + { + try + { + await migrator.Migrate(); + } + catch (MigrationFailed) + { + } + } + + string fileContent = await File.ReadAllTextAsync(Path.Combine(parent.ToString(), knownFolders[Up]!.Path, "2_failing.sql")); + + string[] scripts; + string sql = $"SELECT text_of_script FROM {Context.Syntax.TableWithSchema("grate", "ScriptsRunErrors")}"; + + using (new TransactionScope(TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled)) + { + await using var conn = Context.CreateDbConnection(db); + scripts = (await conn.QueryAsync(sql)).ToArray(); + } + + scripts.First().Should().Be(fileContent); + } + [Test] public void Ensure_Command_Timeout_Fires() { @@ -239,6 +276,13 @@ protected static void CreateInvalidSql(DirectoryInfo root, MigrationsFolder? fol WriteSql(path, "2_failing.sql", dummySql); } + protected void CreateLongInvalidSql(DirectoryInfo root, MigrationsFolder? folder) + { + var dummySql = CreateLongComment(8192) + Environment.NewLine + "SELECT TOP"; + var path = MakeSurePathExists(root, folder); + WriteSql(path, "2_failing.sql", dummySql); + } + private static readonly DirectoryInfo Root = TestConfig.CreateRandomTempDirectory(); private static readonly IFoldersConfiguration Folders = FoldersConfiguration.Default(null); diff --git a/grate.unittests/Generic/Running_MigrationScripts/MigrationsScriptsBase.cs b/grate.unittests/Generic/Running_MigrationScripts/MigrationsScriptsBase.cs index b6595b2a..996b2b2e 100644 --- a/grate.unittests/Generic/Running_MigrationScripts/MigrationsScriptsBase.cs +++ b/grate.unittests/Generic/Running_MigrationScripts/MigrationsScriptsBase.cs @@ -1,4 +1,6 @@ -using System.IO; +using System; +using System.IO; +using System.Text; using grate.Configuration; using grate.unittests.TestInfrastructure; @@ -19,6 +21,40 @@ protected void CreateDummySql(DirectoryInfo? path, string filename = "1_jalla.sq var dummySql = Context.Sql.SelectVersion; WriteSql(path, filename, dummySql); } + + protected void CreateLargeDummySql(DirectoryInfo? path, int size = 8192, string filename = "1_very_large_file.sql") + { + var longComment = CreateLongComment(size); + + var dummySql = longComment + Environment.NewLine + Context.Sql.SelectVersion; + WriteSql(path, filename, dummySql); + } + + protected string CreateLongComment(int size) + { + // Line comment plus blank, plus new line, plus text should be 80 together. + int lineLen = 80 - Context.Sql.LineComment.Length - 1 - Environment.NewLine.Length; + var numLines = size / lineLen; + var rest = size - (lineLen * numLines); + + var filler = new string('Æ', Math.Min(lineLen, size)); + + var builder = new StringBuilder(lineLen * numLines + rest); + for (var i = 0; i < numLines; i++) + { + builder.Append(Context.Sql.LineComment); + builder.Append(' '); + builder.AppendLine(filler); + } + if (rest > 0) + { + builder.Append(Context.Sql.LineComment); + builder.Append(' '); + builder.AppendLine(new string('Ø', rest)); + } + + return builder.ToString(); + } protected void WriteSomeOtherSql(DirectoryInfo? path, string filename = "1_jalla.sql") { diff --git a/grate.unittests/Generic/Running_MigrationScripts/ScriptsRun_Table.cs b/grate.unittests/Generic/Running_MigrationScripts/ScriptsRun_Table.cs index 407931f4..5022823b 100644 --- a/grate.unittests/Generic/Running_MigrationScripts/ScriptsRun_Table.cs +++ b/grate.unittests/Generic/Running_MigrationScripts/ScriptsRun_Table.cs @@ -122,10 +122,38 @@ public async Task Does_not_overwrite_scripts_from_different_folders_with_last_co second.script_name.Should().Be($"sub/dolder/gong/way/{filename}"); second.text_of_script.Should().Be(Context.Syntax.CurrentDatabase); }); - - - } + [Test()] + public async Task Can_handle_large_scripts() + { + var db = TestConfig.RandomDatabase(); + + var parent = TestConfig.CreateRandomTempDirectory(); + var knownFolders = FoldersConfiguration.Default(null); + GrateMigrator? migrator; + + var folder = new DirectoryInfo(Path.Combine(parent.ToString(), knownFolders[Up]!.Path)); + + const string filename = "large_file.sql"; + + CreateLargeDummySql(folder, filename: filename); + await using (migrator = Context.GetMigrator(db, parent, knownFolders)) + { + await migrator.Migrate(); + } + + string fileContent = await File.ReadAllTextAsync(Path.Combine(folder.ToString(), filename)); + + string[] scripts; + string sql = $"SELECT text_of_script FROM {Context.Syntax.TableWithSchema("grate", "ScriptsRun")}"; + + await using (var conn = Context.CreateDbConnection(db)) + { + scripts = (await conn.QueryAsync(sql)).ToArray(); + } + + scripts.First().Should().Be(fileContent); + } } diff --git a/grate.unittests/TestInfrastructure/SqlStatements.cs b/grate.unittests/TestInfrastructure/SqlStatements.cs index f5716658..c36c1967 100644 --- a/grate.unittests/TestInfrastructure/SqlStatements.cs +++ b/grate.unittests/TestInfrastructure/SqlStatements.cs @@ -6,4 +6,5 @@ public record SqlStatements public string SleepTwoSeconds { get; init; } = default!; public string CreateUser { get; init; } = default!; public string GrantAccess { get; init; } = default!; + public string LineComment { get; init; } = "--"; } diff --git a/grate/Migration/AnsiSqlDatabase.cs b/grate/Migration/AnsiSqlDatabase.cs index 7d12997c..bcb29b4a 100644 --- a/grate/Migration/AnsiSqlDatabase.cs +++ b/grate/Migration/AnsiSqlDatabase.cs @@ -18,6 +18,9 @@ namespace grate.Migration; public abstract class AnsiSqlDatabase : IDatabase { + private const string Now = "now"; + private const string User = "usr"; + private string SchemaName { get; set; } = ""; protected GrateConfiguration? Config { get; private set; } @@ -576,16 +579,14 @@ INSERT INTO {ScriptsRunTable} (version_id, script_name, text_of_script, text_hash, one_time_script, entry_date, modified_date, entered_by) VALUES (@versionId, @scriptName, @sql, @hash, @runOnce, @now, @now, @usr)"); - var scriptRun = new - { - versionId, - scriptName, - sql, - hash, - runOnce = Bool(runOnce), - now = DateTime.UtcNow, - usr = Environment.UserName - }; + var scriptRun = new DynamicParameters(); + scriptRun.Add(nameof(versionId), versionId); + scriptRun.Add(nameof(scriptName), scriptName); + scriptRun.Add(nameof(sql), sql, DbType.String); + scriptRun.Add(nameof(hash), hash); + scriptRun.Add(nameof(runOnce), Bool(runOnce)); + scriptRun.Add(Now, DateTime.UtcNow); + scriptRun.Add(User, Environment.UserName); await ExecuteAsync(ActiveConnection, insertSql, scriptRun); } @@ -601,16 +602,14 @@ INSERT INTO {ScriptsRunErrorsTable} var version = await ExecuteScalarAsync(ActiveConnection, versionSql, new { versionId }); - var scriptRunErrors = new - { - version, - scriptName, - sql, - errorSql, - errorMessage, - now = DateTime.UtcNow, - usr = Environment.UserName, - }; + var scriptRunErrors = new DynamicParameters(); + scriptRunErrors.Add(nameof(version), version); + scriptRunErrors.Add(nameof(scriptName), scriptName); + scriptRunErrors.Add(nameof(sql), sql, DbType.String); + scriptRunErrors.Add(nameof(errorSql), errorSql, DbType.String); + scriptRunErrors.Add(nameof(errorMessage), errorMessage, DbType.String); + scriptRunErrors.Add(Now, DateTime.UtcNow); + scriptRunErrors.Add(User, Environment.UserName); await ExecuteAsync(ActiveConnection, insertSql, scriptRunErrors); } From fb07c8e68779d604a96fc17d6f0c7c00572b3c01 Mon Sep 17 00:00:00 2001 From: Ryan Wright Date: Mon, 10 Apr 2023 14:33:01 -0400 Subject: [PATCH 54/63] SQL*Plus compatible separator (#325) --- grate/Infrastructure/OracleSyntax.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/grate/Infrastructure/OracleSyntax.cs b/grate/Infrastructure/OracleSyntax.cs index 9c9f135e..08347504 100644 --- a/grate/Infrastructure/OracleSyntax.cs +++ b/grate/Infrastructure/OracleSyntax.cs @@ -11,7 +11,7 @@ public string StatementSeparatorRegex const string strings = @"(?'[^']*')"; const string dashComments = @"(?--.*$)"; const string starComments = @"(?/\*[\S\s]*?\*/)"; - const string separator = @"(?^|\s)(?GO)(?\s|;|$)"; + const string separator = @"(?^|\s)(?GO|/)(?\s|;|$)"; return strings + "|" + dashComments + "|" + starComments + "|" + separator; } } @@ -54,4 +54,4 @@ FOR ln_cur IN (SELECT sid, serial# FROM v$session WHERE username = usr) public string Quote(string text) => $"\"{text}\""; public string PrimaryKeyConstraint(string tableName, string column) => ""; public string LimitN(string sql, int n) => sql + $"\nLIMIT {n}"; -} \ No newline at end of file +} From 2ef9633cbb0b3db89fef2806f562eaf05df87828 Mon Sep 17 00:00:00 2001 From: "Erik A. Brandstadmoen" Date: Mon, 10 Apr 2023 22:07:47 +0200 Subject: [PATCH 55/63] Added unit tests for statement splitting on Oracle (#328) Maybe they need a bit more refinement, but that'll come later --- .../BatchSplitterReplacer_.cs | 563 ++++++++++++++++++ .../Statement_Splitting/StatementSplitter_.cs | 32 + .../BatchSplitterReplacer_.cs | 4 +- .../OracleSplitterContext.cs | 221 +++++++ ...Context.cs => SqlServerSplitterContext.cs} | 2 +- grate/Infrastructure/OracleSyntax.cs | 2 +- 6 files changed, 820 insertions(+), 4 deletions(-) create mode 100644 grate.unittests/Basic/Infrastructure/Oracle/Statement_Splitting/BatchSplitterReplacer_.cs create mode 100644 grate.unittests/Basic/Infrastructure/Oracle/Statement_Splitting/StatementSplitter_.cs create mode 100644 grate.unittests/TestInfrastructure/OracleSplitterContext.cs rename grate.unittests/TestInfrastructure/{SplitterContext.cs => SqlServerSplitterContext.cs} (98%) diff --git a/grate.unittests/Basic/Infrastructure/Oracle/Statement_Splitting/BatchSplitterReplacer_.cs b/grate.unittests/Basic/Infrastructure/Oracle/Statement_Splitting/BatchSplitterReplacer_.cs new file mode 100644 index 00000000..0909624b --- /dev/null +++ b/grate.unittests/Basic/Infrastructure/Oracle/Statement_Splitting/BatchSplitterReplacer_.cs @@ -0,0 +1,563 @@ +using grate.Infrastructure; +using grate.Migration; +using grate.unittests.TestInfrastructure; +using Microsoft.Extensions.Logging.Abstractions; +using NUnit.Framework; + +// ReSharper disable InconsistentNaming + +namespace grate.unittests.Basic.Infrastructure.Oracle.Statement_Splitting; + +[TestFixture] +[Category("Basic")] +public class BatchSplitterReplacer_ +{ + private const string Batch_terminator_replacement_string = StatementSplitter.BatchTerminatorReplacementString; + + private const string Symbols_to_check = "`~!@#$%^&*()-_+=,.;:'\"[]\\/?<>"; + private const string Words_to_check = "abcdefghijklmnopqrstuvwzyz0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + private static readonly IDatabase Database = new OracleDatabase(NullLogger.Instance); + private static BatchSplitterReplacer Replacer => new(Database.StatementSeparatorRegex, StatementSplitter.BatchTerminatorReplacementString); + + public class should_replace_on + { + [Test] + public void full_statement_without_issue() + { + string sql_to_match = OracleSplitterContext.FullSplitter.PLSqlStatement; + TestContext.WriteLine(sql_to_match); + string sql_statement_scrubbed = Replacer.Replace(sql_to_match); + Assert.AreEqual(OracleSplitterContext.FullSplitter.PLSqlStatementScrubbed, sql_statement_scrubbed); + } + + [Test] + public void slash_with_space() + { + const string sql_to_match = @" / "; + string expected_scrubbed = @" " + Batch_terminator_replacement_string + @" "; + TestContext.WriteLine(sql_to_match); + string sql_statement_scrubbed = Replacer.Replace(sql_to_match); + Assert.AreEqual(expected_scrubbed, sql_statement_scrubbed); + } + + [Test] + public void slash_with_tab() + { + string sql_to_match = @" /" + "\t"; + string expected_scrubbed = @" " + Batch_terminator_replacement_string + "\t"; + TestContext.WriteLine(sql_to_match); + string sql_statement_scrubbed = Replacer.Replace(sql_to_match); + Assert.AreEqual(expected_scrubbed, sql_statement_scrubbed); + } + + [Test] + public void slash_by_itself() + { + const string sql_to_match = @"/"; + string expected_scrubbed = Batch_terminator_replacement_string; + TestContext.WriteLine(sql_to_match); + string sql_statement_scrubbed = Replacer.Replace(sql_to_match); + Assert.AreEqual(expected_scrubbed, sql_statement_scrubbed); + } + + [Test] + public void slash_starting_file() + { + const string sql_to_match = @"/ +whatever"; + string expected_scrubbed = Batch_terminator_replacement_string + @" +whatever"; + TestContext.WriteLine(sql_to_match); + string sql_statement_scrubbed = Replacer.Replace(sql_to_match); + Assert.AreEqual(expected_scrubbed, sql_statement_scrubbed); + } + + [Test] + public void slash_with_new_line() + { + const string sql_to_match = @" / +"; + string expected_scrubbed = @" " + Batch_terminator_replacement_string + @" +"; + TestContext.WriteLine(sql_to_match); + string sql_statement_scrubbed = Replacer.Replace(sql_to_match); + Assert.AreEqual(expected_scrubbed, sql_statement_scrubbed); + } + + [Test] + public void slash_with_one_new_line_after_double_dash_comments() + { + const string sql_to_match = + @"-- +/ +"; + string expected_scrubbed = + @"-- +" + Batch_terminator_replacement_string + @" +"; + TestContext.WriteLine(sql_to_match); + string sql_statement_scrubbed = Replacer.Replace(sql_to_match); + Assert.AreEqual(expected_scrubbed, sql_statement_scrubbed); + } + + [Test] + public void slash_with_one_new_line_after_double_dash_comments_and_words() + { + string sql_to_match = @"-- " + Words_to_check + @" +/ +"; + string expected_scrubbed = @"-- " + Words_to_check + @" +" + Batch_terminator_replacement_string + @" +"; + TestContext.WriteLine(sql_to_match); + string sql_statement_scrubbed = Replacer.Replace(sql_to_match); + Assert.AreEqual(expected_scrubbed, sql_statement_scrubbed); + } + + [Test] + public void slash_with_new_line_after_double_dash_comments_and_symbols() + { + string sql_to_match = @"-- " + Symbols_to_check + @" +/ +"; + string expected_scrubbed = @"-- " + Symbols_to_check + @" +" + Batch_terminator_replacement_string + @" +"; + TestContext.WriteLine(sql_to_match); + string sql_statement_scrubbed = Replacer.Replace(sql_to_match); + Assert.AreEqual(expected_scrubbed, sql_statement_scrubbed); + } + + [Test] + public void slash_on_its_own_line() + { + const string sql_to_match = @" +/ +"; + string expected_scrubbed = @" +" + Batch_terminator_replacement_string + @" +"; + TestContext.WriteLine(sql_to_match); + string sql_statement_scrubbed = Replacer.Replace(sql_to_match); + Assert.AreEqual(expected_scrubbed, sql_statement_scrubbed); + } + + [Test] + public void slash_with_no_line_terminator() + { + const string sql_to_match = @" / "; + string expected_scrubbed = @" " + Batch_terminator_replacement_string + @" "; + TestContext.WriteLine(sql_to_match); + string sql_statement_scrubbed = Replacer.Replace(sql_to_match); + Assert.AreEqual(expected_scrubbed, sql_statement_scrubbed); + } + + [Test] + public void slash_with_words_before() + { + string sql_to_match = Words_to_check + @" / +"; + string expected_scrubbed = Words_to_check + @" " + Batch_terminator_replacement_string + @" +"; + TestContext.WriteLine(sql_to_match); + string sql_statement_scrubbed = Replacer.Replace(sql_to_match); + Assert.AreEqual(expected_scrubbed, sql_statement_scrubbed); + } + + [Test] + public void slash_with_symbols_and_words_before() + { + string sql_to_match = Symbols_to_check + Words_to_check + @" / +"; + string expected_scrubbed = Symbols_to_check + Words_to_check + @" " + + Batch_terminator_replacement_string + @" +"; + TestContext.WriteLine(sql_to_match); + string sql_statement_scrubbed = Replacer.Replace(sql_to_match); + Assert.AreEqual(expected_scrubbed, sql_statement_scrubbed); + } + + [Test] + public void slash_with_words_and_symbols_before() + { + string sql_to_match = Words_to_check + Symbols_to_check + @" / +"; + string expected_scrubbed = Words_to_check + Symbols_to_check + @" " + + Batch_terminator_replacement_string + @" +"; + TestContext.WriteLine(sql_to_match); + string sql_statement_scrubbed = Replacer.Replace(sql_to_match); + Assert.AreEqual(expected_scrubbed, sql_statement_scrubbed); + } + + [Test] + public void slash_with_words_after_on_the_same_line() + { + string sql_to_match = @" / " + Words_to_check; + string expected_scrubbed = @" " + Batch_terminator_replacement_string + @" " + Words_to_check; + TestContext.WriteLine(sql_to_match); + string sql_statement_scrubbed = Replacer.Replace(sql_to_match); + Assert.AreEqual(expected_scrubbed, sql_statement_scrubbed); + } + + [Test] + public void slash_with_words_after_on_the_same_line_including_symbols() + { + string sql_to_match = @" / " + Words_to_check + Symbols_to_check; + string expected_scrubbed = @" " + Batch_terminator_replacement_string + @" " + Words_to_check + + Symbols_to_check; + TestContext.WriteLine(sql_to_match); + string sql_statement_scrubbed = Replacer.Replace(sql_to_match); + Assert.AreEqual(expected_scrubbed, sql_statement_scrubbed); + } + + [Test] + public void slash_with_words_before_and_after_on_the_same_line() + { + string sql_to_match = Words_to_check + @" / " + Words_to_check; + string expected_scrubbed = Words_to_check + @" " + Batch_terminator_replacement_string + @" " + + Words_to_check; + TestContext.WriteLine(sql_to_match); + string sql_statement_scrubbed = Replacer.Replace(sql_to_match); + Assert.AreEqual(expected_scrubbed, sql_statement_scrubbed); + } + + [Test] + public void slash_with_words_before_and_after_on_the_same_line_including_symbols() + { + string sql_to_match = Words_to_check + Symbols_to_check.Replace("'", "").Replace("\"", "") + + " / BOB" + Symbols_to_check; + string expected_scrubbed = Words_to_check + Symbols_to_check.Replace("'", "").Replace("\"", "") + + " " + Batch_terminator_replacement_string + " BOB" + Symbols_to_check; + TestContext.WriteLine(sql_to_match); + string sql_statement_scrubbed = Replacer.Replace(sql_to_match); + Assert.AreEqual(expected_scrubbed, sql_statement_scrubbed); + } + + [Test] + public void slash_after_double_dash_comment_with_single_quote_and_single_quote_after_slash() + { + string sql_to_match = Words_to_check + @" -- ' +/ +select '' +/"; + string expected_scrubbed = Words_to_check + @" -- ' +" + Batch_terminator_replacement_string + @" +select '' +" + Batch_terminator_replacement_string; + TestContext.WriteLine(sql_to_match); + string sql_statement_scrubbed = Replacer.Replace(sql_to_match); + Assert.AreEqual(expected_scrubbed, sql_statement_scrubbed); + } + + [Test] + public void slash_with_comment_after() + { + string sql_to_match = " / -- comment"; + string expected_scrubbed = " " + Batch_terminator_replacement_string + " -- comment"; + TestContext.WriteLine(sql_to_match); + string sql_statement_scrubbed = Replacer.Replace(sql_to_match); + Assert.AreEqual(expected_scrubbed, sql_statement_scrubbed); + } + + [Test] + public void slash_with_semicolon_directly_after() + { + string sql_to_match = "jalla /;"; + string expected_scrubbed = "jalla " + Batch_terminator_replacement_string + ";"; + TestContext.WriteLine(sql_to_match); + string sql_statement_scrubbed = Replacer.Replace(sql_to_match); + Assert.AreEqual(expected_scrubbed, sql_statement_scrubbed); + } + + } + + public class should_not_replace_on + { + + [Test] + public void slash_when_slash_is_the_last_part_of_the_last_word_on_a_line() + { + string sql_to_match = Words_to_check + @"/ +"; + string expected_scrubbed = Words_to_check + @"/ +"; + TestContext.WriteLine(sql_to_match); + string sql_statement_scrubbed = Replacer.Replace(sql_to_match); + Assert.AreEqual(expected_scrubbed, sql_statement_scrubbed); + } + + [Test] + public void slash_with_double_dash_comment_starting_line() + { + string sql_to_match = @"--/ +"; + string expected_scrubbed = @"--/ +"; + TestContext.WriteLine(sql_to_match); + string sql_statement_scrubbed = Replacer.Replace(sql_to_match); + Assert.AreEqual(expected_scrubbed, sql_statement_scrubbed); + } + + [Test] + public void slash_with_double_dash_comment_and_space_starting_line() + { + string sql_to_match = @"-- / +"; + string expected_scrubbed = @"-- / +"; + TestContext.WriteLine(sql_to_match); + string sql_statement_scrubbed = Replacer.Replace(sql_to_match); + Assert.AreEqual(expected_scrubbed, sql_statement_scrubbed); + } + + [Test] + public void slash_with_double_dash_comment_and_space_starting_line_and_words_after_slash() + { + string sql_to_match = @"-- / " + Words_to_check + @" +"; + string expected_scrubbed = @"-- / " + Words_to_check + @" +"; + TestContext.WriteLine(sql_to_match); + string sql_statement_scrubbed = Replacer.Replace(sql_to_match); + Assert.AreEqual(expected_scrubbed, sql_statement_scrubbed); + } + + [Test] + public void slash_with_double_dash_comment_and_space_starting_line_and_symbols_after_slash() + { + string sql_to_match = @"-- / " + Symbols_to_check + @" +"; + string expected_scrubbed = @"-- / " + Symbols_to_check + @" +"; + TestContext.WriteLine(sql_to_match); + string sql_statement_scrubbed = Replacer.Replace(sql_to_match); + Assert.AreEqual(expected_scrubbed, sql_statement_scrubbed); + } + + [Test] + public void slash_with_double_dash_comment_and_tab_starting_line() + { + string sql_to_match = "--" + "\t" + @"/ +"; + string expected_scrubbed = @"--" + "\t" + @"/ +"; + TestContext.WriteLine(sql_to_match); + string sql_statement_scrubbed = Replacer.Replace(sql_to_match); + Assert.AreEqual(expected_scrubbed, sql_statement_scrubbed); + } + + [Test] + public void slash_with_double_dash_comment_and_tab_starting_line_and_words_after_slash() + { + string sql_to_match = @"--" + "\t" + @"/ " + Words_to_check + @" +"; + string expected_scrubbed = @"--" + "\t" + @"/ " + Words_to_check + @" +"; + TestContext.WriteLine(sql_to_match); + string sql_statement_scrubbed = Replacer.Replace(sql_to_match); + Assert.AreEqual(expected_scrubbed, sql_statement_scrubbed); + } + + [Test] + public void slash_with_double_dash_comment_and_tab_starting_line_and_symbols_after_slash() + { + string sql_to_match = @"--" + "\t" + @"/ " + Symbols_to_check + @" +"; + string expected_scrubbed = @"--" + "\t" + @"/ " + Symbols_to_check + @" +"; + TestContext.WriteLine(sql_to_match); + string sql_statement_scrubbed = Replacer.Replace(sql_to_match); + Assert.AreEqual(expected_scrubbed, sql_statement_scrubbed); + } + + [Test] + public void slash_with_double_dash_comment_starting_line_with_words_before_slash() + { + string sql_to_match = @"-- " + Words_to_check + @" / +"; + string expected_scrubbed = @"-- " + Words_to_check + @" / +"; + TestContext.WriteLine(sql_to_match); + string sql_statement_scrubbed = Replacer.Replace(sql_to_match); + Assert.AreEqual(expected_scrubbed, sql_statement_scrubbed); + } + + [Test] + public void slash_when_between_tick_marks() + { + const string sql_to_match = @"' / + '"; + const string expected_scrubbed = @"' / + '"; + TestContext.WriteLine(sql_to_match); + string sql_statement_scrubbed = Replacer.Replace(sql_to_match); + Assert.AreEqual(expected_scrubbed, sql_statement_scrubbed); + } + + [Test] + public void + slash_when_between_tick_marks_with_symbols_and_words_before_ending_on_same_line() + { + string sql_to_match = @"' " + Symbols_to_check.Replace("'", string.Empty) + Words_to_check + @" /'"; + string expected_scrubbed = + @"' " + Symbols_to_check.Replace("'", string.Empty) + Words_to_check + @" /'"; + TestContext.WriteLine(sql_to_match); + string sql_statement_scrubbed = Replacer.Replace(sql_to_match); + Assert.AreEqual(expected_scrubbed, sql_statement_scrubbed); + } + + [Test] + public void slash_when_between_tick_marks_with_symbols_and_words_before() + { + string sql_to_match = @"' " + Symbols_to_check.Replace("'", string.Empty) + Words_to_check + @" / + '"; + string expected_scrubbed = @"' " + Symbols_to_check.Replace("'", string.Empty) + Words_to_check + @" / + '"; + TestContext.WriteLine(sql_to_match); + string sql_statement_scrubbed = Replacer.Replace(sql_to_match); + Assert.AreEqual(expected_scrubbed, sql_statement_scrubbed); + } + + [Test] + public void slash_when_between_tick_marks_with_symbols_and_words_after() + { + string sql_to_match = @"' / + " + Symbols_to_check.Replace("'", string.Empty) + Words_to_check + @"'"; + string expected_scrubbed = @"' / + " + Symbols_to_check.Replace("'", string.Empty) + Words_to_check + @"'"; + TestContext.WriteLine(sql_to_match); + string sql_statement_scrubbed = Replacer.Replace(sql_to_match); + Assert.AreEqual(expected_scrubbed, sql_statement_scrubbed); + } + + [Test] + public void slash_with_double_dash_comment_starting_line_with_symbols_before_slash() + { + string sql_to_match = @"--" + Symbols_to_check + @" / +"; + string expected_scrubbed = @"--" + Symbols_to_check + @" / +"; + TestContext.WriteLine(sql_to_match); + string sql_statement_scrubbed = Replacer.Replace(sql_to_match); + Assert.AreEqual(expected_scrubbed, sql_statement_scrubbed); + } + + [Test] + public void + slash_with_double_dash_comment_starting_line_with_words_and_symbols_before_slash() + { + string sql_to_match = @"--" + Symbols_to_check + Words_to_check + @" / +"; + string expected_scrubbed = @"--" + Symbols_to_check + Words_to_check + @" / +"; + TestContext.WriteLine(sql_to_match); + string sql_statement_scrubbed = Replacer.Replace(sql_to_match); + Assert.AreEqual(expected_scrubbed, sql_statement_scrubbed); + } + + [Test] + public void slash_inside_of_comments() + { + string sql_to_match = @"/* / */"; + string expected_scrubbed = @"/* / */"; + TestContext.WriteLine(sql_to_match); + string sql_statement_scrubbed = Replacer.Replace(sql_to_match); + Assert.AreEqual(expected_scrubbed, sql_statement_scrubbed); + } + + [Test] + public void slash_inside_of_comments_with_a_line_break() + { + string sql_to_match = @"/* / +*/"; + string expected_scrubbed = @"/* / +*/"; + TestContext.WriteLine(sql_to_match); + string sql_statement_scrubbed = Replacer.Replace(sql_to_match); + Assert.AreEqual(expected_scrubbed, sql_statement_scrubbed); + } + + [Test] + public void slash_inside_of_comments_with_words_before() + { + string sql_to_match = + @"/* +" + Words_to_check + @" / + +*/"; + string expected_scrubbed = + @"/* +" + Words_to_check + @" / + +*/"; + TestContext.WriteLine(sql_to_match); + string sql_statement_scrubbed = Replacer.Replace(sql_to_match); + Assert.AreEqual(expected_scrubbed, sql_statement_scrubbed); + } + + [Test] + public void slash_inside_of_comments_with_words_before_on_a_different_line() + { + string sql_to_match = + @"/* +" + Words_to_check + @" +/ + +*/"; + string expected_scrubbed = + @"/* +" + Words_to_check + @" +/ + +*/"; + TestContext.WriteLine(sql_to_match); + string sql_statement_scrubbed = Replacer.Replace(sql_to_match); + Assert.AreEqual(expected_scrubbed, sql_statement_scrubbed); + } + + [Test] + public void slash_inside_of_comments_with_words_before_and_after_on_different_lines() + { + string sql_to_match = + @"/* +" + Words_to_check + @" +/ + +" + Words_to_check + @" +*/"; + string expected_scrubbed = + @"/* +" + Words_to_check + @" +/ + +" + Words_to_check + @" +*/"; + TestContext.WriteLine(sql_to_match); + string sql_statement_scrubbed = Replacer.Replace(sql_to_match); + Assert.AreEqual(expected_scrubbed, sql_statement_scrubbed); + } + + [Test] + public void slash_inside_of_comments_with_symbols_after_on_different_lines() + { + string sql_to_match = + @"/* +/ + +" + Symbols_to_check + @" +*/"; + string expected_scrubbed = + @"/* +/ + +" + Symbols_to_check + @" +*/"; + TestContext.WriteLine(sql_to_match); + string sql_statement_scrubbed = Replacer.Replace(sql_to_match); + Assert.AreEqual(expected_scrubbed, sql_statement_scrubbed); + } + } + +} diff --git a/grate.unittests/Basic/Infrastructure/Oracle/Statement_Splitting/StatementSplitter_.cs b/grate.unittests/Basic/Infrastructure/Oracle/Statement_Splitting/StatementSplitter_.cs new file mode 100644 index 00000000..d5bf5a6c --- /dev/null +++ b/grate.unittests/Basic/Infrastructure/Oracle/Statement_Splitting/StatementSplitter_.cs @@ -0,0 +1,32 @@ +using FluentAssertions; +using grate.Infrastructure; +using grate.Migration; +using Microsoft.Extensions.Logging.Abstractions; +using NUnit.Framework; + +namespace grate.unittests.Basic.Infrastructure.Oracle.Statement_Splitting; + +[TestFixture] +[Category("Basic")] +// ReSharper disable once InconsistentNaming +public class StatementSplitter_ +{ + private static readonly IDatabase Database = new OracleDatabase(NullLogger.Instance); + private static readonly StatementSplitter Splitter = new(Database.StatementSeparatorRegex); + + [Test] + public void Splits_and_removes_GO_statements() + { + var original = @" +SELECT * FROM v$version WHERE banner LIKE 'Oracle%'; + + +/ +SELECT 1 +"; + var batches = Splitter.Split(original); + + batches.Should().HaveCount(2); + } + +} diff --git a/grate.unittests/Basic/Infrastructure/SqlServer/Statement_Splitting/BatchSplitterReplacer_.cs b/grate.unittests/Basic/Infrastructure/SqlServer/Statement_Splitting/BatchSplitterReplacer_.cs index 4cc55838..52510e3d 100644 --- a/grate.unittests/Basic/Infrastructure/SqlServer/Statement_Splitting/BatchSplitterReplacer_.cs +++ b/grate.unittests/Basic/Infrastructure/SqlServer/Statement_Splitting/BatchSplitterReplacer_.cs @@ -25,10 +25,10 @@ public class should_replace_on [Test] public void full_statement_without_issue() { - string sql_to_match = SplitterContext.FullSplitter.tsql_statement; + string sql_to_match = SqlServerSplitterContext.FullSplitter.tsql_statement; TestContext.WriteLine(sql_to_match); string sql_statement_scrubbed = Replacer.Replace(sql_to_match); - Assert.AreEqual(SplitterContext.FullSplitter.tsql_statement_scrubbed, sql_statement_scrubbed); + Assert.AreEqual(SqlServerSplitterContext.FullSplitter.tsql_statement_scrubbed, sql_statement_scrubbed); } [Test] diff --git a/grate.unittests/TestInfrastructure/OracleSplitterContext.cs b/grate.unittests/TestInfrastructure/OracleSplitterContext.cs new file mode 100644 index 00000000..5c5e856d --- /dev/null +++ b/grate.unittests/TestInfrastructure/OracleSplitterContext.cs @@ -0,0 +1,221 @@ +using grate.Infrastructure; +// ReSharper disable StringLiteralTypo + +namespace grate.unittests.TestInfrastructure; + +public static class OracleSplitterContext +{ + + public static class FullSplitter + { + public static string PLSqlStatement = @" +BOB1 +/ + +/* COMMENT */ +BOB2 +/ + +-- / + +BOB3 / + +--`~!@#$%^&*()-_+=,.;:'""[]\/?<> / + +BOB5 + / + +BOB6 +/ + +/* / */ + +BOB7 + +/* + +/ + +*/ + +BOB8 + +-- +/ + +BOB9 + +-- `~!@#$%^&*()-_+=,.;:'""[]\/?<> +/ + +BOB10/ + +CREATE TABLE PO/ +{} + +INSERT INTO PO/ (id,desc) VALUES (1,'/') + +BOB11 + + -- TODO: To be good, there should be type column + +-- dfgjhdfgdjkgk dfgdfg / +BOB12 + +UPDATE Timmy SET id = 'something something /' +UPDATE Timmy SET id = 'something something: /' + +ALTER TABLE Inv.something ADD + gagagag decimal(20, 12) NULL, + asdfasdf DECIMAL(20, 6) NULL, + didbibi decimal(20, 6) NULL, + yeppsasd decimal(20, 6) NULL, + uhuhhh datetime NULL, + slsald varchar(15) NULL, + uhasdf varchar(15) NULL, + daf_asdfasdf DECIMAL(20,6) NULL; +/ + +EXEC @ReturnCode = msdb.dbo.sp_add_jobstep @job_id=@jobId, @step_name=N'Daily job', + @step_id=1, + @cmdexec_success_code=0, + @on_success_action=3, + @on_success_step_id=0, + @on_fail_action=3, + @on_fail_step_id=0, + @retry_attempts=0, + @retry_interval=0, + @os_run_priority=0, @subsystem=N'PLSQL', + @command=N' +dml statements +/ +dml statements ' + +/ + +INSERT [dbo].[Foo] ([Bar]) VALUES (N'hello--world. +Thanks!') +INSERT [dbo].[Foo] ([Bar]) VALUES (N'/ speed racer, / speed racer, / speed racer /!!!!! ') + +/"; + + public static string PLSqlStatementScrubbed = @" +BOB1 +" + StatementSplitter.BatchTerminatorReplacementString + @" + +/* COMMENT */ +BOB2 +" + StatementSplitter.BatchTerminatorReplacementString + @" + +-- / + +BOB3 " + StatementSplitter.BatchTerminatorReplacementString + @" + +--`~!@#$%^&*()-_+=,.;:'""[]\/?<> / + +BOB5 + " + StatementSplitter.BatchTerminatorReplacementString + @" + +BOB6 +" + StatementSplitter.BatchTerminatorReplacementString + @" + +/* / */ + +BOB7 + +/* + +/ + +*/ + +BOB8 + +-- +" + StatementSplitter.BatchTerminatorReplacementString + @" + +BOB9 + +-- `~!@#$%^&*()-_+=,.;:'""[]\/?<> +" + StatementSplitter.BatchTerminatorReplacementString + @" + +BOB10/ + +CREATE TABLE PO/ +{} + +INSERT INTO PO/ (id,desc) VALUES (1,'/') + +BOB11 + + -- TODO: To be good, there should be type column + +-- dfgjhdfgdjkgk dfgdfg / +BOB12 + +UPDATE Timmy SET id = 'something something /' +UPDATE Timmy SET id = 'something something: /' + +ALTER TABLE Inv.something ADD + gagagag decimal(20, 12) NULL, + asdfasdf DECIMAL(20, 6) NULL, + didbibi decimal(20, 6) NULL, + yeppsasd decimal(20, 6) NULL, + uhuhhh datetime NULL, + slsald varchar(15) NULL, + uhasdf varchar(15) NULL, + daf_asdfasdf DECIMAL(20,6) NULL; +" + StatementSplitter.BatchTerminatorReplacementString + @" + +EXEC @ReturnCode = msdb.dbo.sp_add_jobstep @job_id=@jobId, @step_name=N'Daily job', + @step_id=1, + @cmdexec_success_code=0, + @on_success_action=3, + @on_success_step_id=0, + @on_fail_action=3, + @on_fail_step_id=0, + @retry_attempts=0, + @retry_interval=0, + @os_run_priority=0, @subsystem=N'PLSQL', + @command=N' +dml statements +/ +dml statements ' + +" + StatementSplitter.BatchTerminatorReplacementString + @" + +INSERT [dbo].[Foo] ([Bar]) VALUES (N'hello--world. +Thanks!') +INSERT [dbo].[Foo] ([Bar]) VALUES (N'/ speed racer, / speed racer, / speed racer /!!!!! ') + +" + StatementSplitter.BatchTerminatorReplacementString + @""; + + public static string plsql_statement = + @" +SQL1; +; +SQL2; +; +tmpSql := 'DROP SEQUENCE mutatieStockID'; +EXECUTE IMMEDIATE tmpSql; +; +BEGIN +INSERT into Table (columnname) values ("";""); +UPDATE Table set columnname="";""; +END; +"; + public static string plsql_statement_scrubbed = @" +SQL1; +" + StatementSplitter.BatchTerminatorReplacementString + @" +SQL2; +" + StatementSplitter.BatchTerminatorReplacementString + @" +tmpSql := 'DROP SEQUENCE mutatieStockID'; +EXECUTE IMMEDIATE tmpSql; +" + StatementSplitter.BatchTerminatorReplacementString + @" +BEGIN +INSERT into Table (columnname) values ("";""); +UPDATE Table set columnname="";""; +END; +"; + } +} diff --git a/grate.unittests/TestInfrastructure/SplitterContext.cs b/grate.unittests/TestInfrastructure/SqlServerSplitterContext.cs similarity index 98% rename from grate.unittests/TestInfrastructure/SplitterContext.cs rename to grate.unittests/TestInfrastructure/SqlServerSplitterContext.cs index 3a0acd47..b897ae25 100644 --- a/grate.unittests/TestInfrastructure/SplitterContext.cs +++ b/grate.unittests/TestInfrastructure/SqlServerSplitterContext.cs @@ -3,7 +3,7 @@ namespace grate.unittests.TestInfrastructure; -public static class SplitterContext +public static class SqlServerSplitterContext { public static class FullSplitter diff --git a/grate/Infrastructure/OracleSyntax.cs b/grate/Infrastructure/OracleSyntax.cs index 08347504..dbc1a99b 100644 --- a/grate/Infrastructure/OracleSyntax.cs +++ b/grate/Infrastructure/OracleSyntax.cs @@ -11,7 +11,7 @@ public string StatementSeparatorRegex const string strings = @"(?'[^']*')"; const string dashComments = @"(?--.*$)"; const string starComments = @"(?/\*[\S\s]*?\*/)"; - const string separator = @"(?^|\s)(?GO|/)(?\s|;|$)"; + const string separator = @"(?^|\s)(?/)(?\s|;|$)"; return strings + "|" + dashComments + "|" + starComments + "|" + separator; } } From 1dfda1ea91dae44a06128af057f515789ceb4370 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 7 May 2023 20:21:05 +0200 Subject: [PATCH 56/63] build(deps): bump docker/metadata-action from 4.3.0 to 4.4.0 (#331) Bumps [docker/metadata-action](https://github.com/docker/metadata-action) from 4.3.0 to 4.4.0. - [Release notes](https://github.com/docker/metadata-action/releases) - [Commits](https://github.com/docker/metadata-action/compare/507c2f2dc502c992ad446e3d7a5dfbe311567a96...c4ee3adeed93b1fa6a762f209fb01608c1a22f1e) --- updated-dependencies: - dependency-name: docker/metadata-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c73e4b25..a103c3da 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -169,7 +169,7 @@ jobs: - name: Extract metadata (tags, labels) for Docker id: meta - uses: docker/metadata-action@507c2f2dc502c992ad446e3d7a5dfbe311567a96 + uses: docker/metadata-action@c4ee3adeed93b1fa6a762f209fb01608c1a22f1e with: tags: | type=semver,pattern={{version}} From 3036f2a3f965b045544707b6632ecd7641ee0048 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 7 May 2023 20:21:33 +0200 Subject: [PATCH 57/63] build(deps): update FluentAssertions requirement from 6.10.* to 6.11.* (#333) Updates the requirements on [FluentAssertions](https://github.com/fluentassertions/fluentassertions) to permit the latest version. - [Release notes](https://github.com/fluentassertions/fluentassertions/releases) - [Changelog](https://github.com/fluentassertions/fluentassertions/blob/develop/AcceptApiChanges.ps1) - [Commits](https://github.com/fluentassertions/fluentassertions/compare/6.10.0...6.11.0) --- updated-dependencies: - dependency-name: FluentAssertions dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- grate.unittests/grate.unittests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grate.unittests/grate.unittests.csproj b/grate.unittests/grate.unittests.csproj index 41d60b7e..f84941d5 100644 --- a/grate.unittests/grate.unittests.csproj +++ b/grate.unittests/grate.unittests.csproj @@ -15,7 +15,7 @@ all - + From 110e20d814df4cdcf6dc3bc6e048aeae698c8e8f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 May 2023 19:32:23 +0200 Subject: [PATCH 58/63] build(deps): bump Microsoft.NET.Test.Sdk from 17.5.0 to 17.6.0 (#339) Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 17.5.0 to 17.6.0. - [Release notes](https://github.com/microsoft/vstest/releases) - [Changelog](https://github.com/microsoft/vstest/blob/main/docs/releases.md) - [Commits](https://github.com/microsoft/vstest/compare/v17.5.0...v17.6.0) --- updated-dependencies: - dependency-name: Microsoft.NET.Test.Sdk dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- grate.unittests/grate.unittests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grate.unittests/grate.unittests.csproj b/grate.unittests/grate.unittests.csproj index f84941d5..2a30255c 100644 --- a/grate.unittests/grate.unittests.csproj +++ b/grate.unittests/grate.unittests.csproj @@ -7,7 +7,7 @@ - + From fcbb1473297994cbb77c3dcdd8aaad4c8873f4f0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 3 Jun 2023 20:29:06 +0200 Subject: [PATCH 59/63] build(deps): bump Microsoft.NET.Test.Sdk from 17.6.0 to 17.6.1 (#343) Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 17.6.0 to 17.6.1. - [Release notes](https://github.com/microsoft/vstest/releases) - [Changelog](https://github.com/microsoft/vstest/blob/main/docs/releases.md) - [Commits](https://github.com/microsoft/vstest/compare/v17.6.0...v17.6.1) --- updated-dependencies: - dependency-name: Microsoft.NET.Test.Sdk dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- grate.unittests/grate.unittests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grate.unittests/grate.unittests.csproj b/grate.unittests/grate.unittests.csproj index 2a30255c..8b8c55a8 100644 --- a/grate.unittests/grate.unittests.csproj +++ b/grate.unittests/grate.unittests.csproj @@ -7,7 +7,7 @@ - + From a987aada9bc84a0ce8c3d1a6de47f7257481cf3a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 3 Jun 2023 20:29:25 +0200 Subject: [PATCH 60/63] build(deps): update NUnit3TestAdapter requirement from 4.4.* to 4.5.* (#341) Updates the requirements on [NUnit3TestAdapter](https://github.com/nunit/nunit3-vs-adapter) to permit the latest version. - [Release notes](https://github.com/nunit/nunit3-vs-adapter/releases) - [Commits](https://github.com/nunit/nunit3-vs-adapter/compare/V4.4.0...V4.5.0) --- updated-dependencies: - dependency-name: NUnit3TestAdapter dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- grate.unittests/grate.unittests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grate.unittests/grate.unittests.csproj b/grate.unittests/grate.unittests.csproj index 8b8c55a8..dcf3d584 100644 --- a/grate.unittests/grate.unittests.csproj +++ b/grate.unittests/grate.unittests.csproj @@ -10,7 +10,7 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all From b7bba7630277f786323df509c6b39fc819df8aa3 Mon Sep 17 00:00:00 2001 From: Derek Flenniken Date: Sat, 3 Jun 2023 11:34:04 -0700 Subject: [PATCH 61/63] 336 - SearchAllSubdirectoriesInsteadOfTraverse (#337) * option: --searchallinsteadoftraverse, --searchallsubdirectoriesinsteadoftraverse | SearchAllSubdirectoriesInsteadOfTraverse * RoundhousE also logs by file name only in this mode * Ignore extension when sorting scripts --- .../Basic_CommandLineParsing.cs | 9 +++++++ .../Basic/Infrastructure/FileSystem_.cs | 25 ++++++++++++++++++- grate/Commands/MigrateCommand.cs | 7 ++++++ grate/Configuration/GrateConfiguration.cs | 5 ++++ grate/Migration/FileSystem.cs | 12 ++++++--- grate/Migration/GrateMigrator.cs | 14 ++++++----- 6 files changed, 61 insertions(+), 11 deletions(-) diff --git a/grate.unittests/Basic/CommandLineParsing/Basic_CommandLineParsing.cs b/grate.unittests/Basic/CommandLineParsing/Basic_CommandLineParsing.cs index 1ba40bf4..07773957 100644 --- a/grate.unittests/Basic/CommandLineParsing/Basic_CommandLineParsing.cs +++ b/grate.unittests/Basic/CommandLineParsing/Basic_CommandLineParsing.cs @@ -300,6 +300,15 @@ public async Task TestDatabaseType(string args, DatabaseType expected) cfg?.DatabaseType.Should().Be(expected); } + [TestCase("", false)] + [TestCase("--ignoredirectorynames", true)] + [TestCase("--searchallinsteadoftraverse", true)] + [TestCase("--searchallsubdirectoriesinsteadoftraverse", true)] + public async Task IgnoreDirectoryNames(string args, bool expected) + { + var cfg = await ParseGrateConfiguration(args); + cfg?.IgnoreDirectoryNames.Should().Be(expected); + } private static async Task ParseGrateConfiguration(string commandline) { diff --git a/grate.unittests/Basic/Infrastructure/FileSystem_.cs b/grate.unittests/Basic/Infrastructure/FileSystem_.cs index 7b0dfd7f..2dbfa77d 100644 --- a/grate.unittests/Basic/Infrastructure/FileSystem_.cs +++ b/grate.unittests/Basic/Infrastructure/FileSystem_.cs @@ -54,7 +54,30 @@ public void Sorts_enumerated_files_on_sub_path_when_subfolders_are_used() files.First().FullName.Should().Be(Path.Combine(folder1.ToString(), filename2)); files.Last().FullName.Should().Be(Path.Combine(folder2.ToString(), filename1)); } - + + [Test] + public void Sorts_enumerated_files_on_filename_when_directory_names_are_ignored() + { + var parent = TestConfig.CreateRandomTempDirectory(); + var knownFolders = FoldersConfiguration.Default(null); + + var path = Wrap(parent, knownFolders[KnownFolderKeys.Up]!.Path); + + var folder1 = new DirectoryInfo(Path.Combine(path.ToString(), "Init")); + var folder2 = new DirectoryInfo(Path.Combine(path.ToString(), "1.0")); + + string filename1 = "01_Schema.sql"; + string filename2 = "02_SomeChanges.sql"; + + TestConfig.WriteContent(folder1, filename1, "Whatever"); + TestConfig.WriteContent(folder2, filename2, "Whatever"); + + var files = FileSystem.GetFiles(path, "*.sql", true).ToList(); + + files.First().FullName.Should().Be(Path.Combine(folder1.ToString(), filename1)); + files.Last().FullName.Should().Be(Path.Combine(folder2.ToString(), filename2)); + } + protected static DirectoryInfo Wrap(DirectoryInfo root, string? subFolder) => new DirectoryInfo(Path.Combine(root.ToString(), subFolder ?? "")); diff --git a/grate/Commands/MigrateCommand.cs b/grate/Commands/MigrateCommand.cs index 2210c406..20d4c368 100644 --- a/grate/Commands/MigrateCommand.cs +++ b/grate/Commands/MigrateCommand.cs @@ -41,6 +41,7 @@ public MigrateCommand(GrateMigrator mi) : base("Migrates the database") Add(RunAllAnyTimeScripts()); Add(DryRun()); Add(Restore()); + Add(IgnoreDirectoryNames()); Handler = CommandHandler.Create( async () => @@ -300,4 +301,10 @@ private static Option ServerName() => //() => DefaultServerName, "OBSOLETE: Please specify the connection string instead." ); + + private static Option IgnoreDirectoryNames() => + new( + new[] { "--ignoredirectorynames", "--searchallinsteadoftraverse", "--searchallsubdirectoriesinsteadoftraverse" }, + "IgnoreDirectoryNames - By default, scripts are ordered by relative path including subdirectories. This option searches subdirectories, but order is based on filename alone." + ); } diff --git a/grate/Configuration/GrateConfiguration.cs b/grate/Configuration/GrateConfiguration.cs index c96d5a0a..65547f38 100644 --- a/grate/Configuration/GrateConfiguration.cs +++ b/grate/Configuration/GrateConfiguration.cs @@ -122,6 +122,11 @@ public string? AdminConnectionString /// public string? Restore { get; init; } + /// + /// By default, scripts are ordered by relative path including subdirectories. This option searches subdirectories, but order is based on filename alone. + /// + public bool IgnoreDirectoryNames { get; set; } + private static string GetMasterDbName(DatabaseType databaseType) => databaseType switch { DatabaseType.mariadb => "mysql", diff --git a/grate/Migration/FileSystem.cs b/grate/Migration/FileSystem.cs index eb16d9c5..5866da2a 100644 --- a/grate/Migration/FileSystem.cs +++ b/grate/Migration/FileSystem.cs @@ -7,10 +7,14 @@ namespace grate.Migration; public static class FileSystem { - public static IEnumerable GetFiles(DirectoryInfo folderPath, string pattern) + public static IEnumerable GetFiles(DirectoryInfo folderPath, string pattern, bool ignoreDirectoryNames = false) { - return folderPath - .EnumerateFileSystemInfos(pattern, SearchOption.AllDirectories).ToList() - .OrderBy(f => Path.GetRelativePath(folderPath.ToString(), f.FullName), StringComparer.CurrentCultureIgnoreCase); + return ignoreDirectoryNames + ? folderPath + .EnumerateFileSystemInfos(pattern, SearchOption.AllDirectories).ToList() + .OrderBy(f => Path.GetFileNameWithoutExtension(f.FullName), StringComparer.CurrentCultureIgnoreCase) + : folderPath + .EnumerateFileSystemInfos(pattern, SearchOption.AllDirectories).ToList() + .OrderBy(f => Path.GetRelativePath(folderPath.ToString(), f.FullName), StringComparer.CurrentCultureIgnoreCase); } } diff --git a/grate/Migration/GrateMigrator.cs b/grate/Migration/GrateMigrator.cs index baf31df3..90c61474 100644 --- a/grate/Migration/GrateMigrator.cs +++ b/grate/Migration/GrateMigrator.cs @@ -316,7 +316,7 @@ private async Task Process(DirectoryInfo root, MigrationsFolder folder, string c await EnsureConnectionIsOpen(connectionType); var pattern = "*.sql"; - var files = FileSystem.GetFiles(path, pattern); + var files = FileSystem.GetFiles(path, pattern, _migrator.Configuration.IgnoreDirectoryNames); var anySqlRun = false; @@ -325,8 +325,9 @@ private async Task Process(DirectoryInfo root, MigrationsFolder folder, string c var sql = await File.ReadAllTextAsync(file.FullName); // Normalize file names to log, so that results won't vary if you run on *nix VS Windows - var fileNameToLog = string.Join('/', - Path.GetRelativePath(path.ToString(), file.FullName).Split(Path.DirectorySeparatorChar)); + var fileNameToLog = _migrator.Configuration.IgnoreDirectoryNames + ? file.Name + : string.Join('/', Path.GetRelativePath(path.ToString(), file.FullName).Split(Path.DirectorySeparatorChar)); bool theSqlRan = await _migrator.RunSql(sql, fileNameToLog, folder.Type, versionId, _migrator.Configuration.Environment, connectionType, transactionHandling); @@ -360,7 +361,7 @@ private async Task ProcessWithoutLogging(DirectoryInfo root, MigrationsFol await EnsureConnectionIsOpen(connectionType); var pattern = "*.sql"; - var files = FileSystem.GetFiles(path, pattern); + var files = FileSystem.GetFiles(path, pattern, _migrator.Configuration.IgnoreDirectoryNames); var anySqlRun = false; @@ -369,8 +370,9 @@ private async Task ProcessWithoutLogging(DirectoryInfo root, MigrationsFol var sql = await File.ReadAllTextAsync(file.FullName); // Normalize file names to log, so that results won't vary if you run on *nix VS Windows - var fileNameToLog = string.Join('/', - Path.GetRelativePath(path.ToString(), file.FullName).Split(Path.DirectorySeparatorChar)); + var fileNameToLog = _migrator.Configuration.IgnoreDirectoryNames + ? file.Name + : string.Join('/', Path.GetRelativePath(path.ToString(), file.FullName).Split(Path.DirectorySeparatorChar)); bool theSqlRan = await _migrator.RunSqlWithoutLogging(sql, fileNameToLog, _migrator.Configuration.Environment, connectionType, transactionHandling); From a61b6e0472d152df56ae8f4cbe9204026997864b Mon Sep 17 00:00:00 2001 From: "Erik A. Brandstadmoen" Date: Sat, 3 Jun 2023 20:57:34 +0200 Subject: [PATCH 62/63] Sort scripts ignoring .sql extension (discovered as part of #337) --- .../Basic/Infrastructure/FileSystem_.cs | 20 +++++++++++++++++++ grate/Migration/FileSystem.cs | 18 +++++++++++------ 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/grate.unittests/Basic/Infrastructure/FileSystem_.cs b/grate.unittests/Basic/Infrastructure/FileSystem_.cs index 2dbfa77d..fc49706f 100644 --- a/grate.unittests/Basic/Infrastructure/FileSystem_.cs +++ b/grate.unittests/Basic/Infrastructure/FileSystem_.cs @@ -31,6 +31,26 @@ public void Sorts_enumerated_files_on_filename_when_no_subfolders() files.First().FullName.Should().Be(Path.Combine(path.ToString(), filename1)); files.Last().FullName.Should().Be(Path.Combine(path.ToString(), filename2)); } + + [Test] + public void Sorts_enumerated_files_on_filename_without_extension_when_no_subfolders() + { + var parent = TestConfig.CreateRandomTempDirectory(); + var knownFolders = FoldersConfiguration.Default(null); + + var path = Wrap(parent, knownFolders[KnownFolderKeys.Up]!.Path); + + string filename1 = "01_any_filename_and_a_bit_longer.sql"; + string filename2 = "01_any_filename.sql"; + + TestConfig.WriteContent(path, filename1, "Whatever"); + TestConfig.WriteContent(path, filename2, "Whatever"); + + var files = FileSystem.GetFiles(path, "*.sql").ToList(); + + files.First().FullName.Should().Be(Path.Combine(path.ToString(), filename2)); + files.Last().FullName.Should().Be(Path.Combine(path.ToString(), filename1)); + } [Test] public void Sorts_enumerated_files_on_sub_path_when_subfolders_are_used() diff --git a/grate/Migration/FileSystem.cs b/grate/Migration/FileSystem.cs index 5866da2a..0402608e 100644 --- a/grate/Migration/FileSystem.cs +++ b/grate/Migration/FileSystem.cs @@ -1,7 +1,9 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.IO; using System.Linq; +using static System.IO.Path; +using static System.IO.SearchOption; +using static System.StringComparer; namespace grate.Migration; @@ -11,10 +13,14 @@ public static IEnumerable GetFiles(DirectoryInfo folderPath, str { return ignoreDirectoryNames ? folderPath - .EnumerateFileSystemInfos(pattern, SearchOption.AllDirectories).ToList() - .OrderBy(f => Path.GetFileNameWithoutExtension(f.FullName), StringComparer.CurrentCultureIgnoreCase) + .EnumerateFileSystemInfos(pattern, AllDirectories).ToList() + .OrderBy(f => GetFileNameWithoutExtension(f.FullName), CurrentCultureIgnoreCase) : folderPath - .EnumerateFileSystemInfos(pattern, SearchOption.AllDirectories).ToList() - .OrderBy(f => Path.GetRelativePath(folderPath.ToString(), f.FullName), StringComparer.CurrentCultureIgnoreCase); + .EnumerateFileSystemInfos(pattern, AllDirectories).ToList() + .OrderBy(f => + Combine( + GetRelativePath(folderPath.ToString(), GetDirectoryName(f.FullName)!), + GetFileNameWithoutExtension(f.FullName)), + CurrentCultureIgnoreCase); } } From 18709128fa27105276436c4f010d211b77d43b9d Mon Sep 17 00:00:00 2001 From: "Erik A. Brandstadmoen" Date: Sat, 3 Jun 2023 21:48:18 +0200 Subject: [PATCH 63/63] Solve #245 without new command-line parameter (#256) * Solve #245 without new command-line parameter * Ignore the standard tables casing issue for Oracle, as it has never been case sensitive * Fixed casing of INFORMATION_SCHEMA in unit tests --- .../Generic/GenericMigrationTables.cs | 93 +++++++++++++++++++ grate.unittests/Oracle/MigrationTables.cs | 21 ++++- grate.unittests/SqLite/MigrationTables.cs | 11 ++- grate.unittests/SqlServer/MigrationTables.cs | 12 ++- grate.unittests/TestContext.cs | 4 + .../TestInfrastructure/IGrateTestContext.cs | 11 +++ grate/Configuration/GrateConfiguration.cs | 4 + grate/Migration/AnsiSqlDatabase.cs | 65 +++++++++---- grate/Migration/IDatabase.cs | 2 + grate/Migration/MariaDbDatabase.cs | 2 +- grate/Migration/OracleDatabase.cs | 4 +- grate/Migration/PostgreSqlDatabase.cs | 4 +- grate/Migration/SqLiteDatabase.cs | 4 +- grate/Migration/SqlServerDatabase.cs | 2 +- 14 files changed, 210 insertions(+), 29 deletions(-) diff --git a/grate.unittests/Generic/GenericMigrationTables.cs b/grate.unittests/Generic/GenericMigrationTables.cs index 8cf0736e..b66cc018 100644 --- a/grate.unittests/Generic/GenericMigrationTables.cs +++ b/grate.unittests/Generic/GenericMigrationTables.cs @@ -98,7 +98,90 @@ public async Task Migration_does_not_fail_if_table_already_exists(string tableNa Assert.DoesNotThrowAsync(() => migrator.Migrate()); } } + + [TestCase("version")] + [TestCase("vErSiON")] + public async Task Does_not_create_Version_table_if_it_exists_with_another_casing(string existingTable) + { + await CheckTableCasing("Version", existingTable, (config, name) => config.VersionTableName = name); + } + + [TestCase("scriptsrun")] + [TestCase("SCRiptSrUN")] + public async Task Does_not_create_ScriptsRun_table_if_it_exists_with_another_casing(string existingTable) + { + await CheckTableCasing("ScriptsRun", existingTable, (config, name) => config.ScriptsRunTableName = name); + } + + [TestCase("scriptsrunerrors")] + [TestCase("ScripTSRunErrors")] + public async Task Does_not_create_ScriptsRunErrors_table_if_it_exists_with_another_casing(string existingTable) + { + await CheckTableCasing("ScriptsRunErrors", existingTable, (config, name) => config.ScriptsRunErrorsTableName = name); + } + + protected virtual async Task CheckTableCasing(string tableName, string funnyCasing, Action setTableName) + { + var db = TestConfig.RandomDatabase(); + + var parent = TestConfig.CreateRandomTempDirectory(); + var knownFolders = FoldersConfiguration.Default(); + + // Set the version table name to be lower-case first, and run one migration. + var config = Context.GetConfiguration(db, parent, knownFolders); + + setTableName(config, funnyCasing); + + await using (var migrator = Context.GetMigrator(config)) + { + await migrator.Migrate(); + } + + // Check that the table is indeed created with lower-case + var errorCaseCountAfterFirstMigration = await TableCountIn(db, funnyCasing); + var normalCountAfterFirstMigration = await TableCountIn(db, tableName); + Assert.Multiple(() => + { + errorCaseCountAfterFirstMigration.Should().Be(1); + normalCountAfterFirstMigration.Should().Be(0); + }); + + // Run migration again - make sure it does not create the table with different casing too + setTableName(config, tableName); + await using (var migrator = Context.GetMigrator(config)) + { + await migrator.Migrate(); + } + + var errorCaseCountAfterSecondMigration = await TableCountIn(db, funnyCasing); + var normalCountAfterSecondMigration = await TableCountIn(db, tableName); + Assert.Multiple(() => + { + errorCaseCountAfterSecondMigration.Should().Be(1); + normalCountAfterSecondMigration.Should().Be(0); + }); + + } + + private async Task TableCountIn(string db, string tableName) + { + var schemaName = Context.DefaultConfiguration.SchemaName; + var supportsSchemas = Context.DatabaseMigrator.SupportsSchemas; + var fullTableName = supportsSchemas ? tableName : Context.Syntax.TableWithSchema(schemaName, tableName); + var tableSchema = supportsSchemas ? schemaName : db; + + int count; + string countSql = CountTableSql(tableSchema, fullTableName); + + await using (var conn = Context.GetDbConnection(Context.ConnectionString(db))) + { + count = await conn.ExecuteScalarAsync(countSql); + } + + return count; + } + [Test()] public async Task Inserts_version_in_version_table() { @@ -156,4 +239,14 @@ protected static DirectoryInfo MakeSurePathExists(DirectoryInfo? path) private static DirectoryInfo Wrap(DirectoryInfo root, string? relativePath) => new(Path.Combine(root.ToString(), relativePath ?? "")); + protected virtual string CountTableSql(string schemaName, string tableName) + { + return $@" +SELECT count(table_name) FROM INFORMATION_SCHEMA.TABLES +WHERE +table_schema = '{schemaName}' AND +table_name = '{tableName}' +"; + } + } diff --git a/grate.unittests/Oracle/MigrationTables.cs b/grate.unittests/Oracle/MigrationTables.cs index 0e10bb88..5902728b 100644 --- a/grate.unittests/Oracle/MigrationTables.cs +++ b/grate.unittests/Oracle/MigrationTables.cs @@ -1,3 +1,6 @@ +using System; +using System.Threading.Tasks; +using grate.Configuration; using grate.unittests.TestInfrastructure; using NUnit.Framework; @@ -5,7 +8,21 @@ namespace grate.unittests.Oracle; [TestFixture] [Category("Oracle")] -public class MigrationTables: Generic.GenericMigrationTables +public class MigrationTables : Generic.GenericMigrationTables { protected override IGrateTestContext Context => GrateTestContext.Oracle; -} \ No newline at end of file + + protected override Task CheckTableCasing(string tableName, string funnyCasing, Action setTableName) + { + Assert.Ignore("Oracle has never been case-sensitive for grate. No need to introduce that now."); + return Task.CompletedTask; + } + + protected override string CountTableSql(string schemaName, string tableName) + { + return $@" +SELECT COUNT(table_name) FROM user_tables +WHERE +lower(table_name) = '{tableName.ToLowerInvariant()}'"; + } +} diff --git a/grate.unittests/SqLite/MigrationTables.cs b/grate.unittests/SqLite/MigrationTables.cs index e9a9495b..5cb536d2 100644 --- a/grate.unittests/SqLite/MigrationTables.cs +++ b/grate.unittests/SqLite/MigrationTables.cs @@ -8,4 +8,13 @@ namespace grate.unittests.Sqlite; public class MigrationTables: Generic.GenericMigrationTables { protected override IGrateTestContext Context => GrateTestContext.Sqlite; -} \ No newline at end of file + + protected override string CountTableSql(string schemaName, string tableName) + { + return $@" +SELECT COUNT(name) FROM sqlite_master +WHERE type ='table' AND +name = '{tableName}'; +"; + } +} diff --git a/grate.unittests/SqlServer/MigrationTables.cs b/grate.unittests/SqlServer/MigrationTables.cs index 28db250a..aaf55daf 100644 --- a/grate.unittests/SqlServer/MigrationTables.cs +++ b/grate.unittests/SqlServer/MigrationTables.cs @@ -8,4 +8,14 @@ namespace grate.unittests.SqlServer; public class MigrationTables: Generic.GenericMigrationTables { protected override IGrateTestContext Context => GrateTestContext.SqlServer; -} \ No newline at end of file + + protected override string CountTableSql(string schemaName, string tableName) + { + return $@" +SELECT count(table_name) FROM INFORMATION_SCHEMA.TABLES +WHERE +TABLE_SCHEMA = '{schemaName}' AND +TABLE_NAME = '{tableName}' COLLATE Latin1_General_CS_AS +"; + } +} diff --git a/grate.unittests/TestContext.cs b/grate.unittests/TestContext.cs index 8e0b04b0..5e75cb7b 100644 --- a/grate.unittests/TestContext.cs +++ b/grate.unittests/TestContext.cs @@ -1,4 +1,8 @@ using grate.unittests.TestInfrastructure; +using NUnit.Framework; + +// There are some parallelism issues, but this does not solve it +//[assembly:LevelOfParallelism(1)] namespace grate.unittests; diff --git a/grate.unittests/TestInfrastructure/IGrateTestContext.cs b/grate.unittests/TestInfrastructure/IGrateTestContext.cs index 1756a8c0..dd8d72b1 100644 --- a/grate.unittests/TestInfrastructure/IGrateTestContext.cs +++ b/grate.unittests/TestInfrastructure/IGrateTestContext.cs @@ -56,6 +56,17 @@ DefaultConfiguration with SqlFilesDirectory = sqlFilesDirectory }; + public GrateConfiguration GetConfiguration(string databaseName, DirectoryInfo sqlFilesDirectory, + IFoldersConfiguration knownFolders, string? env, bool runInTransaction) => + DefaultConfiguration with + { + ConnectionString = ConnectionString(databaseName), + Folders = knownFolders, + Environment = env != null ? new GrateEnvironment(env) : null, + Transaction = runInTransaction, + SqlFilesDirectory = sqlFilesDirectory + }; + public GrateMigrator GetMigrator(GrateConfiguration config) { var factory = Substitute.For(); diff --git a/grate/Configuration/GrateConfiguration.cs b/grate/Configuration/GrateConfiguration.cs index 65547f38..fd646cc4 100644 --- a/grate/Configuration/GrateConfiguration.cs +++ b/grate/Configuration/GrateConfiguration.cs @@ -26,6 +26,10 @@ public record GrateConfiguration public string? ConnectionString { get; init; } = null; public string SchemaName { get; init; } = "grate"; + + public string ScriptsRunTableName { get; set; } = "ScriptsRun"; + public string ScriptsRunErrorsTableName { get; set; } = "ScriptsRunErrors"; + public string VersionTableName { get; set; } = "Version"; public string? AdminConnectionString { diff --git a/grate/Migration/AnsiSqlDatabase.cs b/grate/Migration/AnsiSqlDatabase.cs index bcb29b4a..72f38ec1 100644 --- a/grate/Migration/AnsiSqlDatabase.cs +++ b/grate/Migration/AnsiSqlDatabase.cs @@ -46,14 +46,18 @@ protected AnsiSqlDatabase(ILogger logger, ISyntax syntax) .Split("=", TrimEntries | RemoveEmptyEntries).Last(); public abstract bool SupportsDdlTransactions { get; } - protected abstract bool SupportsSchemas { get; } + public abstract bool SupportsSchemas { get; } public bool SplitBatchStatements => true; public string StatementSeparatorRegex => _syntax.StatementSeparatorRegex; - public string ScriptsRunTable => _syntax.TableWithSchema(SchemaName, "ScriptsRun"); - public string ScriptsRunErrorsTable => _syntax.TableWithSchema(SchemaName, "ScriptsRunErrors"); - public string VersionTable => _syntax.TableWithSchema(SchemaName, "Version"); + public string ScriptsRunTable => _syntax.TableWithSchema(SchemaName, ScriptsRunTableName); + public string ScriptsRunErrorsTable => _syntax.TableWithSchema(SchemaName, ScriptsRunErrorsTableName); + public string VersionTable => _syntax.TableWithSchema(SchemaName, VersionTableName); + + private string ScriptsRunTableName { get; set; } + private string ScriptsRunErrorsTableName { get; set; } + private string VersionTableName { get; set; } public virtual Task InitializeConnections(GrateConfiguration configuration) { @@ -61,11 +65,23 @@ public virtual Task InitializeConnections(GrateConfiguration configuration) ConnectionString = configuration.ConnectionString; AdminConnectionString = configuration.AdminConnectionString; + SchemaName = configuration.SchemaName; + + VersionTableName = configuration.VersionTableName; + ScriptsRunTableName = configuration.ScriptsRunTableName; + ScriptsRunErrorsTableName = configuration.ScriptsRunErrorsTableName; + Config = configuration; + return Task.CompletedTask; } + private async Task ExistingOrDefault(string schemaName, string tableName) => + await ExistingTable(schemaName, tableName) ?? tableName; + + + private string? AdminConnectionString { get; set; } protected string? ConnectionString { get; set; } @@ -266,6 +282,10 @@ private async Task RunSchemaExists() protected virtual async Task CreateScriptsRunTable() { + // Update scripts run table name with the correct casing, should it differ from the standard + + ScriptsRunTableName = await ExistingOrDefault(SchemaName, ScriptsRunTableName); + string createSql = $@" CREATE TABLE {ScriptsRunTable}( {_syntax.PrimaryKeyColumn("id")}, @@ -288,6 +308,9 @@ protected virtual async Task CreateScriptsRunTable() protected virtual async Task CreateScriptsRunErrorsTable() { + // Update scripts run errors table name with the correct casing, should it differ from the standard + ScriptsRunErrorsTableName = await ExistingOrDefault(SchemaName, ScriptsRunErrorsTableName); + string createSql = $@" CREATE TABLE {ScriptsRunErrorsTable}( {_syntax.PrimaryKeyColumn("id")}, @@ -310,6 +333,9 @@ protected virtual async Task CreateScriptsRunErrorsTable() protected virtual async Task CreateVersionTable() { + // Update version table name with the correct casing, should it differ from the standard + VersionTableName = await ExistingOrDefault(SchemaName, VersionTableName); + string createSql = $@" CREATE TABLE {VersionTable}( {_syntax.PrimaryKeyColumn("id")}, @@ -320,6 +346,7 @@ protected virtual async Task CreateVersionTable() entered_by {_syntax.VarcharType}(50) NULL {_syntax.PrimaryKeyConstraint("Version", "id")} )"; + if (!await VersionTableExists()) { await ExecuteNonQuery(ActiveConnection, createSql, Config?.CommandTimeout); @@ -338,21 +365,25 @@ ALTER TABLE {VersionTable} } } - protected async Task ScriptsRunTableExists() => await TableExists(SchemaName, "ScriptsRun"); - protected async Task ScriptsRunErrorsTableExists() => await TableExists(SchemaName, "ScriptsRunErrors"); - public async Task VersionTableExists() => await TableExists(SchemaName, "Version"); - protected async Task StatusColumnInVersionTableExists() => await ColumnExists(SchemaName, "Version", "status"); + protected async Task ScriptsRunTableExists() => (await ExistingTable(SchemaName, ScriptsRunTableName) is not null) ; + protected async Task ScriptsRunErrorsTableExists() => (await ExistingTable(SchemaName, ScriptsRunErrorsTableName) is not null); + public async Task VersionTableExists() => (await ExistingTable(SchemaName, VersionTableName) is not null); + + protected async Task StatusColumnInVersionTableExists() => await ColumnExists(SchemaName, VersionTableName, "status"); - public async Task TableExists(string schemaName, string tableName) + public async Task ExistingTable(string schemaName, string tableName) { var fullTableName = SupportsSchemas ? tableName : _syntax.TableWithSchema(schemaName, tableName); var tableSchema = SupportsSchemas ? schemaName : DatabaseName; - + string existsSql = ExistsSql(tableSchema, fullTableName); var res = await ExecuteScalarAsync(ActiveConnection, existsSql); - return !DBNull.Value.Equals(res) && res is not null; + var name = (!DBNull.Value.Equals(res) && res is not null) ? (string) res : null; + + var prefix = SupportsSchemas ? string.Empty : _syntax.TableWithSchema(schemaName, string.Empty); + return name?[prefix.Length..] ; } private async Task ColumnExists(string schemaName, string tableName, string columnName) @@ -369,10 +400,10 @@ private async Task ColumnExists(string schemaName, string tableName, strin protected virtual string ExistsSql(string tableSchema, string fullTableName) { return $@" -SELECT * FROM INFORMATION_SCHEMA.TABLES +SELECT table_name FROM INFORMATION_SCHEMA.TABLES WHERE -TABLE_SCHEMA = '{tableSchema}' AND -TABLE_NAME = '{fullTableName}' +LOWER(TABLE_SCHEMA) = LOWER('{tableSchema}') AND +LOWER(TABLE_NAME) = LOWER('{fullTableName}') "; } @@ -381,9 +412,9 @@ protected virtual string ExistsSql(string tableSchema, string fullTableName, str return $@" SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE -TABLE_SCHEMA = '{tableSchema}' AND -TABLE_NAME = '{fullTableName}' AND -COLUMN_NAME = '{columnName}' +LOWER(TABLE_SCHEMA) = LOWER('{tableSchema}') AND +LOWER(TABLE_NAME) = LOWER('{fullTableName}') AND +LOWER(COLUMN_NAME) = LOWER('{columnName}') "; } diff --git a/grate/Migration/IDatabase.cs b/grate/Migration/IDatabase.cs index 708b13d7..26755cef 100644 --- a/grate/Migration/IDatabase.cs +++ b/grate/Migration/IDatabase.cs @@ -17,6 +17,7 @@ public interface IDatabase : IAsyncDisposable public string ScriptsRunErrorsTable { get; } public string VersionTable { get; } DbConnection ActiveConnection { set; } + bool SupportsSchemas { get; } Task InitializeConnections(GrateConfiguration configuration); Task OpenConnection(); @@ -48,4 +49,5 @@ Task InsertScriptRun(string scriptName, string? sql, string hash, bool runOnce, void SetDefaultConnectionActive(); Task OpenNewActiveConnection(); Task OpenActiveConnection(); + Task ExistingTable(string schemaName, string tableName); } diff --git a/grate/Migration/MariaDbDatabase.cs b/grate/Migration/MariaDbDatabase.cs index 527e2e06..93cda516 100644 --- a/grate/Migration/MariaDbDatabase.cs +++ b/grate/Migration/MariaDbDatabase.cs @@ -15,7 +15,7 @@ public MariaDbDatabase(ILogger logger) { } public override bool SupportsDdlTransactions => false; - protected override bool SupportsSchemas => false; + public override bool SupportsSchemas => false; protected override DbConnection GetSqlConnection(string? connectionString) => new MySqlConnection(connectionString); public override Task RestoreDatabase(string backupPath) diff --git a/grate/Migration/OracleDatabase.cs b/grate/Migration/OracleDatabase.cs index 940f28fa..576a1d1d 100644 --- a/grate/Migration/OracleDatabase.cs +++ b/grate/Migration/OracleDatabase.cs @@ -23,13 +23,13 @@ public OracleDatabase(ILogger logger) } public override bool SupportsDdlTransactions => false; - protected override bool SupportsSchemas => false; + public override bool SupportsSchemas => false; protected override DbConnection GetSqlConnection(string? connectionString) => new OracleConnection(connectionString); protected override string ExistsSql(string tableSchema, string fullTableName) => $@" -SELECT * FROM user_tables +SELECT table_name FROM user_tables WHERE lower(table_name) = '{fullTableName.ToLowerInvariant()}' "; diff --git a/grate/Migration/PostgreSqlDatabase.cs b/grate/Migration/PostgreSqlDatabase.cs index 2d7e7377..1dfb5afc 100644 --- a/grate/Migration/PostgreSqlDatabase.cs +++ b/grate/Migration/PostgreSqlDatabase.cs @@ -13,11 +13,11 @@ public PostgreSqlDatabase(ILogger logger) { } public override bool SupportsDdlTransactions => true; - protected override bool SupportsSchemas => true; + public override bool SupportsSchemas => true; protected override DbConnection GetSqlConnection(string? connectionString) => new NpgsqlConnection(connectionString); public override Task RestoreDatabase(string backupPath) { throw new System.NotImplementedException("Restoring a database from file is not currently supported for Postgresql."); } -} \ No newline at end of file +} diff --git a/grate/Migration/SqLiteDatabase.cs b/grate/Migration/SqLiteDatabase.cs index c087af24..25f1d607 100644 --- a/grate/Migration/SqLiteDatabase.cs +++ b/grate/Migration/SqLiteDatabase.cs @@ -17,14 +17,14 @@ public SqliteDatabase(ILogger logger) { } public override bool SupportsDdlTransactions => false; - protected override bool SupportsSchemas => false; + public override bool SupportsSchemas => false; protected override DbConnection GetSqlConnection(string? connectionString) => new SqliteConnection(connectionString); protected override string ExistsSql(string tableSchema, string fullTableName) => $@" SELECT name FROM sqlite_master WHERE type ='table' AND -name = '{fullTableName}'; +LOWER(name) = LOWER('{fullTableName}'); "; protected override string ExistsSql(string tableSchema, string fullTableName, string columnName) => diff --git a/grate/Migration/SqlServerDatabase.cs b/grate/Migration/SqlServerDatabase.cs index 51c39f6a..bf98f57c 100644 --- a/grate/Migration/SqlServerDatabase.cs +++ b/grate/Migration/SqlServerDatabase.cs @@ -16,7 +16,7 @@ public SqlServerDatabase(ILogger logger) { } public override bool SupportsDdlTransactions => true; - protected override bool SupportsSchemas => true; + public override bool SupportsSchemas => true; protected override DbConnection GetSqlConnection(string? connectionString) { // If pooling is not explicitly mentioned in the connection string, turn it off, as enabling it