From dccadb32fda71f4686ce1106c8576e8a876f4642 Mon Sep 17 00:00:00 2001 From: JaBistDuNarrisch Date: Wed, 13 Aug 2025 08:38:05 +0200 Subject: [PATCH 01/27] Refactoring test class structure part I --- .../Base/TransformationProviderBase.cs | 472 +-------------- ...ationProviderGenericMiscConstraintBase.cs} | 4 +- .../TransformationProviderGenericMiscTests.cs | 537 ++++++++++++++++++ .../OracleTransformationProviderTestBase.cs | 21 - .../PostgreSQLTransformationProviderTest.cs | 2 +- ...ionProvider_GetColumnsDefaultValueTests.cs | 58 +- ...QLiteTransformationProviderGenericTests.cs | 3 +- src/Migrator/Providers/Dialect.cs | 2 +- .../Impl/PostgreSQL/PostgreSQLDialect.cs | 5 + .../SqlServerTransformationProvider.cs | 15 +- 10 files changed, 591 insertions(+), 528 deletions(-) rename src/Migrator.Tests/Providers/{Base/TransformationProviderConstraintBase.cs => Generic/TransformationProviderGenericMiscConstraintBase.cs} (98%) create mode 100644 src/Migrator.Tests/Providers/Generic/TransformationProviderGenericMiscTests.cs diff --git a/src/Migrator.Tests/Providers/Base/TransformationProviderBase.cs b/src/Migrator.Tests/Providers/Base/TransformationProviderBase.cs index 6ad3c5c1..b929bd2c 100644 --- a/src/Migrator.Tests/Providers/Base/TransformationProviderBase.cs +++ b/src/Migrator.Tests/Providers/Base/TransformationProviderBase.cs @@ -1,468 +1,50 @@ using System; using System.Data; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; using DotNetProjects.Migrator.Framework; -using Migrator.Tests.Providers.Base; +using DotNetProjects.Migrator.Providers; +using DotNetProjects.Migrator.Providers.Impl.Oracle; +using DryIoc; +using Migrator.Tests.Database; +using Migrator.Tests.Database.Interfaces; +using Migrator.Tests.Settings; +using Migrator.Tests.Settings.Config; +using Migrator.Tests.Settings.Models; using NUnit.Framework; -namespace Migrator.Tests.Providers; +namespace Migrator.Tests.Providers.Base; /// /// Base class for Provider tests for all non-constraint oriented tests. /// public abstract class TransformationProviderBase : TransformationProviderSimpleBase { - [Test] - public void TableExistsWorks() + protected async Task StartOracleProvider() { - Assert.That(Provider.TableExists("gadadadadseeqwe"), Is.False); - Assert.That(Provider.TableExists("TestTwo"), Is.True); - } - - [Test] - public void ColumnExistsWorks() - { - Assert.That(Provider.ColumnExists("gadadadadseeqwe", "eqweqeq"), Is.False); - Assert.That(Provider.ColumnExists("TestTwo", "eqweqeq"), Is.False); - Assert.That(Provider.ColumnExists("TestTwo", "Id"), Is.True); - } - - [Test] - public void CanExecuteBadSqlForNonCurrentProvider() - { - Provider["foo"].ExecuteNonQuery("select foo from bar 123"); - } - - [Test] - public void TableCanBeAdded() - { - AddTable(); - Assert.That(Provider.TableExists("Test"), Is.True); - } + using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10)); + var configReader = new ConfigurationReader(); - [Test] - public void GetTablesWorks() - { - foreach (var name in Provider.GetTables()) - { - Provider.Logger.Log("Table: {0}", name); - } - - Assert.That(1, Is.EqualTo(Provider.GetTables().Length)); - AddTable(); - Assert.That(2, Is.EqualTo(Provider.GetTables().Length)); - } + var databaseConnectionConfig = configReader.GetDatabaseConnectionConfigById(DatabaseConnectionConfigIds.OracleId); - [Test] - public void GetColumnsReturnsProperCount() - { - AddTable(); - var cols = Provider.GetColumns("Test"); - - Assert.That(cols, Is.Not.Null); - Assert.That(6, Is.EqualTo(cols.Length)); - } - - [Test] - public void GetColumnsContainsProperNullInformation() - { - AddTableWithPrimaryKey(); - var cols = Provider.GetColumns("Test"); - Assert.That(cols, Is.Not.Null); + var connectionString = databaseConnectionConfig?.ConnectionString; - foreach (var column in cols) + if (string.IsNullOrEmpty(connectionString)) { - if (column.Name == "name") - { - Assert.That((column.ColumnProperty & ColumnProperty.NotNull) == ColumnProperty.NotNull, Is.True); - } - else if (column.Name == "Title") - { - Assert.That((column.ColumnProperty & ColumnProperty.Null) == ColumnProperty.Null, Is.True); - } + throw new IgnoreException($"No Oracle {nameof(DatabaseConnectionConfig.ConnectionString)} is set."); } - } - - [Test] - public void CanAddTableWithPrimaryKey() - { - AddTableWithPrimaryKey(); - Assert.That(Provider.TableExists("Test"), Is.True); - } - - [Test] - public void RemoveTable() - { - AddTable(); - Provider.RemoveTable("Test"); - Assert.That(Provider.TableExists("Test"), Is.False); - } - - [Test] - public virtual void RenameTableThatExists() - { - AddTable(); - Provider.RenameTable("Test", "Test_Rename"); - - Assert.That(Provider.TableExists("Test_Rename"), Is.True); - Assert.That(Provider.TableExists("Test"), Is.False); - Provider.RemoveTable("Test_Rename"); - } - - [Test] - public void RenameTableToExistingTable() - { - AddTable(); - Assert.Throws(() => - { - Provider.RenameTable("Test", "TestTwo"); - }); - } - [Test] - public void RenameColumnThatExists() - { - AddTable(); - Provider.RenameColumn("Test", "name", "name_rename"); - - Assert.That(Provider.ColumnExists("Test", "name_rename"), Is.True); - Assert.That(Provider.ColumnExists("Test", "name"), Is.False); - } + DbProviderFactories.RegisterFactory("Oracle.ManagedDataAccess.Client", () => Oracle.ManagedDataAccess.Client.OracleClientFactory.Instance); - [Test] - public void RenameColumnToExistingColumn() - { - AddTable(); - Assert.Throws(() => - { - Provider.RenameColumn("Test", "Title", "name"); - }); - } - - [Test] - public void RemoveUnexistingTable() - { - var exception = Assert.Catch(() => Provider.RemoveTable("abc")); - var expectedMessage = "Table with name 'abc' does not exist to rename"; - - Assert.That(exception.Message, Is.EqualTo(expectedMessage)); - } - - [Test] - public void AddColumn() - { - Provider.AddColumn("TestTwo", "Test", DbType.String, 50); - Assert.That(Provider.ColumnExists("TestTwo", "Test"), Is.True); - } - - [Test] - public void ChangeColumn() - { - Provider.ChangeColumn("TestTwo", new Column("TestId", DbType.String, 50)); - Assert.That(Provider.ColumnExists("TestTwo", "TestId"), Is.True); - Provider.Insert("TestTwo", ["Id", "TestId"], [1, "Not an Int val."]); - } - - [Test] - public void ChangeColumn_FromNullToNull() - { - Provider.ChangeColumn("TestTwo", new Column("TestId", DbType.String, 50, ColumnProperty.Null)); - Provider.ChangeColumn("TestTwo", new Column("TestId", DbType.String, 50, ColumnProperty.Null)); - Provider.ChangeColumn("TestTwo", new Column("TestId", DbType.String, 50, ColumnProperty.Null)); - Provider.Insert("TestTwo", ["Id", "TestId"], [2, "Not an Int val."]); - } - - [Test] - public void AddDecimalColumn() - { - Provider.AddColumn("TestTwo", "TestDecimal", DbType.Decimal, 38); - Assert.That(Provider.ColumnExists("TestTwo", "TestDecimal"), Is.True); - } - - [Test] - public void AddColumnWithDefault() - { - Provider.AddColumn("TestTwo", "TestWithDefault", DbType.Int32, 50, 0, 10); - Assert.That(Provider.ColumnExists("TestTwo", "TestWithDefault"), Is.True); - } - - [Test] - public void AddColumnWithDefaultButNoSize() - { - Provider.AddColumn("TestTwo", "TestWithDefault", DbType.Int32, 10); - Assert.That(Provider.ColumnExists("TestTwo", "TestWithDefault"), Is.True); - - Provider.AddColumn("TestTwo", "TestWithDefaultString", DbType.String, "'foo'"); - Assert.That(Provider.ColumnExists("TestTwo", "TestWithDefaultString"), Is.True); - } - - [Test] - public void AddBooleanColumnWithDefault() - { - Provider.AddColumn("TestTwo", "TestBoolean", DbType.Boolean, 0, 0, false); - Assert.That(Provider.ColumnExists("TestTwo", "TestBoolean"), Is.True); - } - - [Test] - public void CanGetNullableFromProvider() - { - Provider.AddColumn("TestTwo", "NullableColumn", DbType.String, 30, ColumnProperty.Null); - var columns = Provider.GetColumns("TestTwo"); - - foreach (var column in columns) - { - if (column.Name == "NullableColumn") - { - Assert.That((column.ColumnProperty & ColumnProperty.Null) == ColumnProperty.Null, Is.True); - } - } - } - - [Test] - public void RemoveColumn() - { - AddColumn(); - Provider.RemoveColumn("TestTwo", "Test"); - Assert.That(Provider.ColumnExists("TestTwo", "Test"), Is.False); - } - - [Test] - public void RemoveColumnWithDefault() - { - AddColumnWithDefault(); - Provider.RemoveColumn("TestTwo", "TestWithDefault"); - Assert.That(Provider.ColumnExists("TestTwo", "TestWithDefault"), Is.False); - } + using var container = new Container(); + container.RegisterDatabaseIntegrationTestService(); + var databaseIntegrationTestServiceFactory = container.Resolve(); + var oracleIntegrationTestService = databaseIntegrationTestServiceFactory.Create(DatabaseProviderType.Oracle); + var databaseInfo = await oracleIntegrationTestService.CreateTestDatabaseAsync(databaseConnectionConfig, cts.Token); - [Test] - public void RemoveUnexistingColumn() - { - var exception1 = Assert.Throws(() => Provider.RemoveColumn("TestTwo", "abc")); - var exception2 = Assert.Throws(() => Provider.RemoveColumn("abc", "abc")); - - Assert.That(exception1.Message, Is.EqualTo("The table 'TestTwo' does not have a column named 'abc'")); - Assert.That(exception2.Message, Is.EqualTo("The table 'abc' does not exist")); - } - - /// - /// Supprimer une colonne bit causait une erreur à cause - /// de la valeur par défaut. - /// - [Test] - public void RemoveBoolColumn() - { - AddTable(); - Provider.AddColumn("Test", "Inactif", DbType.Boolean); - Assert.That(Provider.ColumnExists("Test", "Inactif"), Is.True); - - Provider.RemoveColumn("Test", "Inactif"); - Assert.That(Provider.ColumnExists("Test", "Inactif"), Is.False); - } - - [Test] - public void HasColumn() - { - AddColumn(); - Assert.That(Provider.ColumnExists("TestTwo", "Test"), Is.True); - Assert.That(Provider.ColumnExists("TestTwo", "TestPasLa"), Is.False); - } - - [Test] - public void HasTable() - { - Assert.That(Provider.TableExists("TestTwo"), Is.True); - } - - [Test] - public void AppliedMigrations() - { - Assert.That(Provider.TableExists("SchemaInfo"), Is.False); - - // Check that a "get" call works on the first run. - Assert.That(0, Is.EqualTo(Provider.AppliedMigrations.Count)); - Assert.That(Provider.TableExists("SchemaInfo"), Is.True, "No SchemaInfo table created"); - - // Check that a "set" called after the first run works. - Provider.MigrationApplied(1, null); - Assert.That(1, Is.EqualTo(Provider.AppliedMigrations[0])); - - Provider.RemoveTable("SchemaInfo"); - // Check that a "set" call works on the first run. - Provider.MigrationApplied(1, null); - Assert.That(1, Is.EqualTo(Provider.AppliedMigrations[0])); - Assert.That(Provider.TableExists("SchemaInfo"), Is.True, "No SchemaInfo table created"); - } - - - [Test] - public void CommitTwice() - { - Provider.Commit(); - Assert.That(0, Is.EqualTo(Provider.AppliedMigrations.Count)); - Provider.Commit(); - } - - [Test] - public void InsertData() - { - Provider.Insert("TestTwo", ["Id", "TestId"], [1, 1]); - Provider.Insert("TestTwo", ["Id", "TestId"], [2, 2]); - - using var cmd = Provider.CreateCommand(); - using var reader = Provider.Select(cmd, "TestId", "TestTwo"); - var vals = GetVals(reader); - - Assert.That(Array.Exists(vals, delegate (int val) { return val == 1; }), Is.True); - Assert.That(Array.Exists(vals, delegate (int val) { return val == 2; }), Is.True); - } - - [Test] - public void CanInsertNullData() - { - AddTable(); - - Provider.Insert("Test", ["Id", "Title"], [1, "foo"]); - Provider.Insert("Test", ["Id", "Title"], [2, null]); - - using var cmd = Provider.CreateCommand(); - using var reader = Provider.Select(cmd, "Title", "Test"); - var vals = GetStringVals(reader); - - Assert.That(Array.Exists(vals, delegate (string val) { return val == "foo"; }), Is.True); - Assert.That(Array.Exists(vals, delegate (string val) { return val == null; }), Is.True); - } - - [Test] - public void CanInsertDataWithSingleQuotes() - { - // Arrange - const string testString = "Test string with ' (single quote)"; - AddTable(); - Provider.Insert("Test", ["Id", "Title"], [1, testString]); - - using var cmd = Provider.CreateCommand(); - using var reader = Provider.Select(cmd, "Title", "Test"); - - Assert.That(reader.Read(), Is.True); - Assert.That(testString, Is.EqualTo(reader.GetString(0))); - Assert.That(reader.Read(), Is.False); - } - - [Test] - public void DeleteData() - { - InsertData(); - Provider.Delete("TestTwo", "TestId", "1"); - using var cmd = Provider.CreateCommand(); - using var reader = Provider.Select(cmd, "TestId", "TestTwo"); - Assert.That(reader.Read(), Is.True); - Assert.That(2, Is.EqualTo(Convert.ToInt32(reader[0]))); - Assert.That(reader.Read(), Is.False); - } - - [Test] - public void DeleteDataWithArrays() - { - InsertData(); - - Provider.Delete("TestTwo", ["TestId"], [1]); - - using var cmd = Provider.CreateCommand(); - using var reader = Provider.Select(cmd, "TestId", "TestTwo"); - - Assert.That(reader.Read(), Is.True); - Assert.That(2, Is.EqualTo(Convert.ToInt32(reader[0]))); - Assert.That(reader.Read(), Is.False); - } - - [Test] - public void UpdateData() - { - Provider.Insert("TestTwo", ["Id", "TestId"], [20, 1]); - Provider.Insert("TestTwo", ["Id", "TestId"], [21, 2]); - - Provider.Update("TestTwo", ["TestId"], [3]); - using var cmd = Provider.CreateCommand(); - using var reader = Provider.Select(cmd, "TestId", "TestTwo"); - var vals = GetVals(reader); - - Assert.That(Array.Exists(vals, delegate (int val) { return val == 3; }), Is.True); - Assert.That(Array.Exists(vals, delegate (int val) { return val == 1; }), Is.False); - Assert.That(Array.Exists(vals, delegate (int val) { return val == 2; }), Is.False); - } - - [Test] - public void CanUpdateWithNullData() - { - AddTable(); - Provider.Insert("Test", ["Id", "Title"], [1, "foo"]); - Provider.Insert("Test", ["Id", "Title"], [2, null]); - - Provider.Update("Test", ["Title"], [null]); - using var cmd = Provider.CreateCommand(); - using var reader = Provider.Select(cmd, "Title", "Test"); - var vals = GetStringVals(reader); - - Assert.That(vals[0], Is.Null); - Assert.That(vals[1], Is.Null); - } - - [Test] - public void UpdateDataWithWhere() - { - Provider.Insert("TestTwo", ["Id", "TestId"], [10, 1]); - Provider.Insert("TestTwo", ["Id", "TestId"], [11, 2]); - - Provider.Update("TestTwo", ["TestId"], [3], "TestId='1'"); - using var cmd = Provider.CreateCommand(); - using var reader = Provider.Select(cmd, "TestId", "TestTwo"); - var vals = GetVals(reader); - - Assert.That(Array.Exists(vals, delegate (int val) { return val == 3; }), Is.True); - Assert.That(Array.Exists(vals, delegate (int val) { return val == 2; }), Is.True); - Assert.That(Array.Exists(vals, delegate (int val) { return val == 1; }), Is.False); - } - - [Test] - public void AddIndex() - { - var indexName = "test_index"; - - Assert.That(Provider.IndexExists("TestTwo", indexName), Is.False); - Provider.AddIndex(indexName, "TestTwo", "Id", "TestId"); - Assert.That(Provider.IndexExists("TestTwo", indexName), Is.True); - } - - [Test] - public void RemoveIndex() - { - var indexName = "test_index"; - - Assert.That(Provider.IndexExists("TestTwo", indexName), Is.False); - Provider.AddIndex(indexName, "TestTwo", "Id", "TestId"); - Provider.RemoveIndex("TestTwo", indexName); - Assert.That(Provider.IndexExists("TestTwo", indexName), Is.False); - } - - - private int[] GetVals(IDataReader reader) - { - var vals = new int[2]; - Assert.That(reader.Read(), Is.True); - vals[0] = Convert.ToInt32(reader[0]); - Assert.That(reader.Read(), Is.True); - vals[1] = Convert.ToInt32(reader[0]); - - return vals; - } - - private string[] GetStringVals(IDataReader reader) - { - var vals = new string[2]; - Assert.That(reader.Read(), Is.True); - vals[0] = reader[0] as string; - Assert.That(reader.Read(), Is.True); - vals[1] = reader[0] as string; + Provider = new OracleTransformationProvider(new OracleDialect(), databaseInfo.DatabaseConnectionConfig.ConnectionString, null, "default", "Oracle.ManagedDataAccess.Client"); - return vals; + Provider.BeginTransaction(); } } diff --git a/src/Migrator.Tests/Providers/Base/TransformationProviderConstraintBase.cs b/src/Migrator.Tests/Providers/Generic/TransformationProviderGenericMiscConstraintBase.cs similarity index 98% rename from src/Migrator.Tests/Providers/Base/TransformationProviderConstraintBase.cs rename to src/Migrator.Tests/Providers/Generic/TransformationProviderGenericMiscConstraintBase.cs index 8a096bcd..17ea3d12 100644 --- a/src/Migrator.Tests/Providers/Base/TransformationProviderConstraintBase.cs +++ b/src/Migrator.Tests/Providers/Generic/TransformationProviderGenericMiscConstraintBase.cs @@ -4,12 +4,12 @@ using DotNetProjects.Migrator.Framework; using NUnit.Framework; -namespace Migrator.Tests.Providers; +namespace Migrator.Tests.Providers.Generic; /// /// Base class for Provider tests for all tests including constraint oriented tests. /// -public abstract class TransformationProviderConstraintBase : TransformationProviderBase +public abstract class TransformationProviderGenericMiscConstraintBase : TransformationProviderGenericMiscTests { public void AddForeignKey() { diff --git a/src/Migrator.Tests/Providers/Generic/TransformationProviderGenericMiscTests.cs b/src/Migrator.Tests/Providers/Generic/TransformationProviderGenericMiscTests.cs new file mode 100644 index 00000000..4b5a200c --- /dev/null +++ b/src/Migrator.Tests/Providers/Generic/TransformationProviderGenericMiscTests.cs @@ -0,0 +1,537 @@ +using System; +using System.Data; +using System.Linq; +using DotNetProjects.Migrator.Framework; +using Migrator.Tests.Providers.Base; +using NUnit.Framework; + +namespace Migrator.Tests.Providers.Generic; + +/// +/// Base class for provider tests. +/// +public abstract class TransformationProviderGenericMiscTests : TransformationProviderSimpleBase +{ + [Test] + public void GetColumns_DefaultValues_Succeeds() + { + // Arrange + var dateTimeDefaultValue = new DateTime(2000, 1, 2, 3, 4, 5, DateTimeKind.Utc); + var guidDefaultValue = Guid.NewGuid(); + var decimalDefaultValue = 14.56565m; + + const string testTableName = "MyDefaultTestTable"; + + const string dateTimeColumnName1 = "datetimecolumn1"; + const string dateTimeColumnName2 = "datetimecolumn2"; + const string decimalColumnName1 = "decimalcolumn"; + const string guidColumnName1 = "guidcolumn1"; + const string booleanColumnName1 = "booleancolumn1"; + const string int32ColumnName1 = "int32column1"; + const string int64ColumnName1 = "int64column1"; + const string int64ColumnName2 = "int64column2"; + const string stringColumnName1 = "stringcolumn1"; + const string binaryColumnName1 = "binarycolumn1"; + const string doubleColumnName1 = "doublecolumn1"; + + // Should be extended by remaining types + Provider.AddTable(testTableName, + new Column(dateTimeColumnName1, DbType.DateTime, dateTimeDefaultValue), + new Column(dateTimeColumnName2, DbType.DateTime2, dateTimeDefaultValue), + new Column(decimalColumnName1, DbType.Decimal, decimalDefaultValue), + new Column(guidColumnName1, DbType.Guid, guidDefaultValue), + + // other boolean default values are tested in another test + new Column(booleanColumnName1, DbType.Boolean, true), + + new Column(int32ColumnName1, DbType.Int32, defaultValue: 43), + new Column(int64ColumnName1, DbType.Int64, defaultValue: 88), + new Column(int64ColumnName2, DbType.Int64, defaultValue: 0), + new Column(stringColumnName1, DbType.String, defaultValue: "Hello"), + new Column(binaryColumnName1, DbType.Binary, defaultValue: new byte[] { 12, 32, 34 }), + new Column(doubleColumnName1, DbType.Double, defaultValue: 84.874596565) + ); + + // Act + var columns = Provider.GetColumns(testTableName); + + // Assert + var dateTimeColumn1 = columns.Single(x => x.Name == dateTimeColumnName1); + var dateTimeColumn2 = columns.Single(x => x.Name == dateTimeColumnName2); + var decimalColumn1 = columns.Single(x => x.Name == decimalColumnName1); + var guidColumn1 = columns.Single(x => x.Name == guidColumnName1); + var booleanColumn1 = columns.Single(x => x.Name == booleanColumnName1); + var int32Column1 = columns.Single(x => x.Name == int32ColumnName1); + var int64Column1 = columns.Single(x => x.Name == int64ColumnName1); + var int64Column2 = columns.Single(x => x.Name == int64ColumnName2); + var stringColumn1 = columns.Single(x => x.Name == stringColumnName1); + var binarycolumn1 = columns.Single(x => x.Name == binaryColumnName1); + var doubleColumn1 = columns.Single(x => x.Name == doubleColumnName1); + + Assert.That(dateTimeColumn1.DefaultValue, Is.EqualTo(dateTimeDefaultValue)); + Assert.That(dateTimeColumn2.DefaultValue, Is.EqualTo(dateTimeDefaultValue)); + Assert.That(decimalColumn1.DefaultValue, Is.EqualTo(decimalDefaultValue)); + Assert.That(guidColumn1.DefaultValue, Is.EqualTo(guidDefaultValue)); + Assert.That(booleanColumn1.DefaultValue, Is.True); + Assert.That(int32Column1.DefaultValue, Is.EqualTo(43)); + Assert.That(int64Column1.DefaultValue, Is.EqualTo(88)); + Assert.That(stringColumn1.DefaultValue, Is.EqualTo("Hello")); + Assert.That(binarycolumn1.DefaultValue, Is.EqualTo(new byte[] { 12, 32, 34 })); + Assert.That(doubleColumn1.DefaultValue, Is.EqualTo(84.874596565)); + } + + [Test] + public void TableExistsWorks() + { + Assert.That(Provider.TableExists("gadadadadseeqwe"), Is.False); + Assert.That(Provider.TableExists("TestTwo"), Is.True); + } + + [Test] + public void ColumnExistsWorks() + { + Assert.That(Provider.ColumnExists("gadadadadseeqwe", "eqweqeq"), Is.False); + Assert.That(Provider.ColumnExists("TestTwo", "eqweqeq"), Is.False); + Assert.That(Provider.ColumnExists("TestTwo", "Id"), Is.True); + } + + [Test] + public void CanExecuteBadSqlForNonCurrentProvider() + { + Provider["foo"].ExecuteNonQuery("select foo from bar 123"); + } + + [Test] + public void TableCanBeAdded() + { + AddTable(); + Assert.That(Provider.TableExists("Test"), Is.True); + } + + [Test] + public void GetTablesWorks() + { + foreach (var name in Provider.GetTables()) + { + Provider.Logger.Log("Table: {0}", name); + } + + Assert.That(1, Is.EqualTo(Provider.GetTables().Length)); + AddTable(); + Assert.That(2, Is.EqualTo(Provider.GetTables().Length)); + } + + [Test] + public void GetColumnsReturnsProperCount() + { + AddTable(); + var cols = Provider.GetColumns("Test"); + + Assert.That(cols, Is.Not.Null); + Assert.That(6, Is.EqualTo(cols.Length)); + } + + [Test] + public void GetColumnsContainsProperNullInformation() + { + AddTableWithPrimaryKey(); + var cols = Provider.GetColumns("Test"); + Assert.That(cols, Is.Not.Null); + + foreach (var column in cols) + { + if (column.Name == "name") + { + Assert.That((column.ColumnProperty & ColumnProperty.NotNull) == ColumnProperty.NotNull, Is.True); + } + else if (column.Name == "Title") + { + Assert.That((column.ColumnProperty & ColumnProperty.Null) == ColumnProperty.Null, Is.True); + } + } + } + + [Test] + public void CanAddTableWithPrimaryKey() + { + AddTableWithPrimaryKey(); + Assert.That(Provider.TableExists("Test"), Is.True); + } + + [Test] + public void RemoveTable() + { + AddTable(); + Provider.RemoveTable("Test"); + Assert.That(Provider.TableExists("Test"), Is.False); + } + + [Test] + public virtual void RenameTableThatExists() + { + AddTable(); + Provider.RenameTable("Test", "Test_Rename"); + + Assert.That(Provider.TableExists("Test_Rename"), Is.True); + Assert.That(Provider.TableExists("Test"), Is.False); + Provider.RemoveTable("Test_Rename"); + } + + [Test] + public void RenameTableToExistingTable() + { + AddTable(); + Assert.Throws(() => + { + Provider.RenameTable("Test", "TestTwo"); + }); + } + + [Test] + public void RenameColumnThatExists() + { + AddTable(); + Provider.RenameColumn("Test", "name", "name_rename"); + + Assert.That(Provider.ColumnExists("Test", "name_rename"), Is.True); + Assert.That(Provider.ColumnExists("Test", "name"), Is.False); + } + + [Test] + public void RenameColumnToExistingColumn() + { + AddTable(); + Assert.Throws(() => + { + Provider.RenameColumn("Test", "Title", "name"); + }); + } + + [Test] + public void RemoveUnexistingTable() + { + var exception = Assert.Catch(() => Provider.RemoveTable("abc")); + var expectedMessage = "Table with name 'abc' does not exist to rename"; + + Assert.That(exception.Message, Is.EqualTo(expectedMessage)); + } + + [Test] + public void AddColumn() + { + Provider.AddColumn("TestTwo", "Test", DbType.String, 50); + Assert.That(Provider.ColumnExists("TestTwo", "Test"), Is.True); + } + + [Test] + public void ChangeColumn() + { + Provider.ChangeColumn("TestTwo", new Column("TestId", DbType.String, 50)); + Assert.That(Provider.ColumnExists("TestTwo", "TestId"), Is.True); + Provider.Insert("TestTwo", ["Id", "TestId"], [1, "Not an Int val."]); + } + + [Test] + public void ChangeColumn_FromNullToNull() + { + Provider.ChangeColumn("TestTwo", new Column("TestId", DbType.String, 50, ColumnProperty.Null)); + Provider.ChangeColumn("TestTwo", new Column("TestId", DbType.String, 50, ColumnProperty.Null)); + Provider.ChangeColumn("TestTwo", new Column("TestId", DbType.String, 50, ColumnProperty.Null)); + Provider.Insert("TestTwo", ["Id", "TestId"], [2, "Not an Int val."]); + } + + [Test] + public void AddDecimalColumn() + { + Provider.AddColumn("TestTwo", "TestDecimal", DbType.Decimal, 38); + Assert.That(Provider.ColumnExists("TestTwo", "TestDecimal"), Is.True); + } + + [Test] + public void AddColumnWithDefault() + { + Provider.AddColumn("TestTwo", "TestWithDefault", DbType.Int32, 50, 0, 10); + Assert.That(Provider.ColumnExists("TestTwo", "TestWithDefault"), Is.True); + } + + [Test] + public void AddColumnWithDefaultButNoSize() + { + Provider.AddColumn("TestTwo", "TestWithDefault", DbType.Int32, 10); + Assert.That(Provider.ColumnExists("TestTwo", "TestWithDefault"), Is.True); + + Provider.AddColumn("TestTwo", "TestWithDefaultString", DbType.String, "'foo'"); + Assert.That(Provider.ColumnExists("TestTwo", "TestWithDefaultString"), Is.True); + } + + [Test] + public void AddBooleanColumnWithDefault() + { + Provider.AddColumn("TestTwo", "TestBoolean", DbType.Boolean, 0, 0, false); + Assert.That(Provider.ColumnExists("TestTwo", "TestBoolean"), Is.True); + } + + [Test] + public void CanGetNullableFromProvider() + { + Provider.AddColumn("TestTwo", "NullableColumn", DbType.String, 30, ColumnProperty.Null); + var columns = Provider.GetColumns("TestTwo"); + + foreach (var column in columns) + { + if (column.Name == "NullableColumn") + { + Assert.That((column.ColumnProperty & ColumnProperty.Null) == ColumnProperty.Null, Is.True); + } + } + } + + [Test] + public void RemoveColumn() + { + AddColumn(); + Provider.RemoveColumn("TestTwo", "Test"); + Assert.That(Provider.ColumnExists("TestTwo", "Test"), Is.False); + } + + [Test] + public void RemoveColumnWithDefault() + { + AddColumnWithDefault(); + Provider.RemoveColumn("TestTwo", "TestWithDefault"); + Assert.That(Provider.ColumnExists("TestTwo", "TestWithDefault"), Is.False); + } + + [Test] + public void RemoveUnexistingColumn() + { + var exception1 = Assert.Throws(() => Provider.RemoveColumn("TestTwo", "abc")); + var exception2 = Assert.Throws(() => Provider.RemoveColumn("abc", "abc")); + + Assert.That(exception1.Message, Is.EqualTo("The table 'TestTwo' does not have a column named 'abc'")); + Assert.That(exception2.Message, Is.EqualTo("The table 'abc' does not exist")); + } + + /// + /// Supprimer une colonne bit causait une erreur à cause + /// de la valeur par défaut. + /// + [Test] + public void RemoveBoolColumn() + { + AddTable(); + Provider.AddColumn("Test", "Inactif", DbType.Boolean); + Assert.That(Provider.ColumnExists("Test", "Inactif"), Is.True); + + Provider.RemoveColumn("Test", "Inactif"); + Assert.That(Provider.ColumnExists("Test", "Inactif"), Is.False); + } + + [Test] + public void HasColumn() + { + AddColumn(); + Assert.That(Provider.ColumnExists("TestTwo", "Test"), Is.True); + Assert.That(Provider.ColumnExists("TestTwo", "TestPasLa"), Is.False); + } + + [Test] + public void HasTable() + { + Assert.That(Provider.TableExists("TestTwo"), Is.True); + } + + [Test] + public void AppliedMigrations() + { + Assert.That(Provider.TableExists("SchemaInfo"), Is.False); + + // Check that a "get" call works on the first run. + Assert.That(0, Is.EqualTo(Provider.AppliedMigrations.Count)); + Assert.That(Provider.TableExists("SchemaInfo"), Is.True, "No SchemaInfo table created"); + + // Check that a "set" called after the first run works. + Provider.MigrationApplied(1, null); + Assert.That(1, Is.EqualTo(Provider.AppliedMigrations[0])); + + Provider.RemoveTable("SchemaInfo"); + // Check that a "set" call works on the first run. + Provider.MigrationApplied(1, null); + Assert.That(1, Is.EqualTo(Provider.AppliedMigrations[0])); + Assert.That(Provider.TableExists("SchemaInfo"), Is.True, "No SchemaInfo table created"); + } + + + [Test] + public void CommitTwice() + { + Provider.Commit(); + Assert.That(0, Is.EqualTo(Provider.AppliedMigrations.Count)); + Provider.Commit(); + } + + [Test] + public void InsertData() + { + Provider.Insert("TestTwo", ["Id", "TestId"], [1, 1]); + Provider.Insert("TestTwo", ["Id", "TestId"], [2, 2]); + + using var cmd = Provider.CreateCommand(); + using var reader = Provider.Select(cmd, "TestId", "TestTwo"); + var vals = GetVals(reader); + + Assert.That(Array.Exists(vals, delegate (int val) { return val == 1; }), Is.True); + Assert.That(Array.Exists(vals, delegate (int val) { return val == 2; }), Is.True); + } + + [Test] + public void CanInsertNullData() + { + AddTable(); + + Provider.Insert("Test", ["Id", "Title"], [1, "foo"]); + Provider.Insert("Test", ["Id", "Title"], [2, null]); + + using var cmd = Provider.CreateCommand(); + using var reader = Provider.Select(cmd, "Title", "Test"); + var vals = GetStringVals(reader); + + Assert.That(Array.Exists(vals, delegate (string val) { return val == "foo"; }), Is.True); + Assert.That(Array.Exists(vals, delegate (string val) { return val == null; }), Is.True); + } + + [Test] + public void CanInsertDataWithSingleQuotes() + { + // Arrange + const string testString = "Test string with ' (single quote)"; + AddTable(); + Provider.Insert("Test", ["Id", "Title"], [1, testString]); + + using var cmd = Provider.CreateCommand(); + using var reader = Provider.Select(cmd, "Title", "Test"); + + Assert.That(reader.Read(), Is.True); + Assert.That(testString, Is.EqualTo(reader.GetString(0))); + Assert.That(reader.Read(), Is.False); + } + + [Test] + public void DeleteData() + { + InsertData(); + Provider.Delete("TestTwo", "TestId", "1"); + using var cmd = Provider.CreateCommand(); + using var reader = Provider.Select(cmd, "TestId", "TestTwo"); + Assert.That(reader.Read(), Is.True); + Assert.That(2, Is.EqualTo(Convert.ToInt32(reader[0]))); + Assert.That(reader.Read(), Is.False); + } + + [Test] + public void DeleteDataWithArrays() + { + InsertData(); + + Provider.Delete("TestTwo", ["TestId"], [1]); + + using var cmd = Provider.CreateCommand(); + using var reader = Provider.Select(cmd, "TestId", "TestTwo"); + + Assert.That(reader.Read(), Is.True); + Assert.That(2, Is.EqualTo(Convert.ToInt32(reader[0]))); + Assert.That(reader.Read(), Is.False); + } + + [Test] + public void UpdateData() + { + Provider.Insert("TestTwo", ["Id", "TestId"], [20, 1]); + Provider.Insert("TestTwo", ["Id", "TestId"], [21, 2]); + + Provider.Update("TestTwo", ["TestId"], [3]); + using var cmd = Provider.CreateCommand(); + using var reader = Provider.Select(cmd, "TestId", "TestTwo"); + var vals = GetVals(reader); + + Assert.That(Array.Exists(vals, delegate (int val) { return val == 3; }), Is.True); + Assert.That(Array.Exists(vals, delegate (int val) { return val == 1; }), Is.False); + Assert.That(Array.Exists(vals, delegate (int val) { return val == 2; }), Is.False); + } + + [Test] + public void CanUpdateWithNullData() + { + AddTable(); + Provider.Insert("Test", ["Id", "Title"], [1, "foo"]); + Provider.Insert("Test", ["Id", "Title"], [2, null]); + + Provider.Update("Test", ["Title"], [null]); + using var cmd = Provider.CreateCommand(); + using var reader = Provider.Select(cmd, "Title", "Test"); + var vals = GetStringVals(reader); + + Assert.That(vals[0], Is.Null); + Assert.That(vals[1], Is.Null); + } + + [Test] + public void UpdateDataWithWhere() + { + Provider.Insert("TestTwo", ["Id", "TestId"], [10, 1]); + Provider.Insert("TestTwo", ["Id", "TestId"], [11, 2]); + + Provider.Update("TestTwo", ["TestId"], [3], "TestId='1'"); + using var cmd = Provider.CreateCommand(); + using var reader = Provider.Select(cmd, "TestId", "TestTwo"); + var vals = GetVals(reader); + + Assert.That(Array.Exists(vals, delegate (int val) { return val == 3; }), Is.True); + Assert.That(Array.Exists(vals, delegate (int val) { return val == 2; }), Is.True); + Assert.That(Array.Exists(vals, delegate (int val) { return val == 1; }), Is.False); + } + + [Test] + public void AddIndex() + { + var indexName = "test_index"; + + Assert.That(Provider.IndexExists("TestTwo", indexName), Is.False); + Provider.AddIndex(indexName, "TestTwo", "Id", "TestId"); + Assert.That(Provider.IndexExists("TestTwo", indexName), Is.True); + } + + [Test] + public void RemoveIndex() + { + var indexName = "test_index"; + + Assert.That(Provider.IndexExists("TestTwo", indexName), Is.False); + Provider.AddIndex(indexName, "TestTwo", "Id", "TestId"); + Provider.RemoveIndex("TestTwo", indexName); + Assert.That(Provider.IndexExists("TestTwo", indexName), Is.False); + } + + + private int[] GetVals(IDataReader reader) + { + var vals = new int[2]; + Assert.That(reader.Read(), Is.True); + vals[0] = Convert.ToInt32(reader[0]); + Assert.That(reader.Read(), Is.True); + vals[1] = Convert.ToInt32(reader[0]); + + return vals; + } + + private string[] GetStringVals(IDataReader reader) + { + var vals = new string[2]; + Assert.That(reader.Read(), Is.True); + vals[0] = reader[0] as string; + Assert.That(reader.Read(), Is.True); + vals[1] = reader[0] as string; + + return vals; + } +} diff --git a/src/Migrator.Tests/Providers/OracleProvider/Base/OracleTransformationProviderTestBase.cs b/src/Migrator.Tests/Providers/OracleProvider/Base/OracleTransformationProviderTestBase.cs index a7179ec1..e1d7e65e 100644 --- a/src/Migrator.Tests/Providers/OracleProvider/Base/OracleTransformationProviderTestBase.cs +++ b/src/Migrator.Tests/Providers/OracleProvider/Base/OracleTransformationProviderTestBase.cs @@ -19,28 +19,7 @@ public class OracleTransformationProviderTestBase : TransformationProviderSimple [SetUp] public async Task SetUpAsync() { - using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10)); - var configReader = new ConfigurationReader(); - var databaseConnectionConfig = configReader.GetDatabaseConnectionConfigById(DatabaseConnectionConfigIds.OracleId); - - var connectionString = databaseConnectionConfig?.ConnectionString; - - if (string.IsNullOrEmpty(connectionString)) - { - throw new IgnoreException($"No Oracle {nameof(DatabaseConnectionConfig.ConnectionString)} is set."); - } - - DbProviderFactories.RegisterFactory("Oracle.ManagedDataAccess.Client", () => Oracle.ManagedDataAccess.Client.OracleClientFactory.Instance); - - using var container = new Container(); - container.RegisterDatabaseIntegrationTestService(); - var databaseIntegrationTestServiceFactory = container.Resolve(); - var oracleIntegrationTestService = databaseIntegrationTestServiceFactory.Create(DatabaseProviderType.Oracle); - var databaseInfo = await oracleIntegrationTestService.CreateTestDatabaseAsync(databaseConnectionConfig, cts.Token); - - Provider = new OracleTransformationProvider(new OracleDialect(), databaseInfo.DatabaseConnectionConfig.ConnectionString, null, "default", "Oracle.ManagedDataAccess.Client"); - Provider.BeginTransaction(); AddDefaultTable(); } diff --git a/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProviderTest.cs b/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProviderTest.cs index a1e973ec..f24a1289 100644 --- a/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProviderTest.cs +++ b/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProviderTest.cs @@ -4,7 +4,7 @@ using Migrator.Tests.Settings.Config; using NUnit.Framework; -namespace Migrator.Tests.Providers; +namespace Migrator.Tests.Providers.PostgreSQL; [TestFixture] [Category("Postgre")] diff --git a/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_GetColumnsDefaultValueTests.cs b/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_GetColumnsDefaultValueTests.cs index 0e9447b6..0d76beda 100644 --- a/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_GetColumnsDefaultValueTests.cs +++ b/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_GetColumnsDefaultValueTests.cs @@ -2,6 +2,7 @@ using System.Data; using System.Linq; using DotNetProjects.Migrator.Framework; +using Migrator.Tests.Providers.Base; using NUnit.Framework; namespace Migrator.Tests.Providers.PostgreSQL; @@ -10,47 +11,19 @@ namespace Migrator.Tests.Providers.PostgreSQL; [Category("Postgre")] public class PostgreSQLTransformationProvider_GetColumnsDefaultTypeTests : PostgreSQLTransformationProviderTestBase { - private const decimal DecimalDefaultValue = 14.56565m; - + /// + /// More tests for GetColumns in + /// [Test] - public void GetColumns_DefaultValues_Succeeds() + public void GetColumns_DefaultValuesInterval_Succeeds() { // Arrange - var dateTimeDefaultValue = new DateTime(2000, 1, 2, 3, 4, 5, DateTimeKind.Utc); - var guidDefaultValue = Guid.NewGuid(); - const string testTableName = "MyDefaultTestTable"; - - const string dateTimeColumnName1 = "datetimecolumn1"; - const string dateTimeColumnName2 = "datetimecolumn2"; - const string decimalColumnName1 = "decimalcolumn"; - const string guidColumnName1 = "guidcolumn1"; - const string booleanColumnName1 = "booleancolumn1"; - const string int32ColumnName1 = "int32column1"; - const string int64ColumnName1 = "int64column1"; - const string int64ColumnName2 = "int64column2"; - const string stringColumnName1 = "stringcolumn1"; - const string binaryColumnName1 = "binarycolumn1"; - const string doubleColumnName1 = "doublecolumn1"; const string intervalColumnName1 = "intervalcolumn1"; const string intervalColumnName2 = "intervalcolumn2"; // Should be extended by remaining types Provider.AddTable(testTableName, - new Column(dateTimeColumnName1, DbType.DateTime, dateTimeDefaultValue), - new Column(dateTimeColumnName2, DbType.DateTime2, dateTimeDefaultValue), - new Column(decimalColumnName1, DbType.Decimal, DecimalDefaultValue), - new Column(guidColumnName1, DbType.Guid, guidDefaultValue), - - // other boolean default values are tested in another test - new Column(booleanColumnName1, DbType.Boolean, true), - - new Column(int32ColumnName1, DbType.Int32, defaultValue: 43), - new Column(int64ColumnName1, DbType.Int64, defaultValue: 88), - new Column(int64ColumnName2, DbType.Int64, defaultValue: 0), - new Column(stringColumnName1, DbType.String, defaultValue: "Hello"), - new Column(binaryColumnName1, DbType.Binary, defaultValue: new byte[] { 12, 32, 34 }), - new Column(doubleColumnName1, DbType.Double, defaultValue: 84.874596565), new Column(intervalColumnName1, MigratorDbType.Interval, defaultValue: new TimeSpan(100000, 3, 4, 5, 666)), new Column(intervalColumnName2, MigratorDbType.Interval, defaultValue: new TimeSpan(0, 0, 0, 0, 666)) ); @@ -59,30 +32,9 @@ public void GetColumns_DefaultValues_Succeeds() var columns = Provider.GetColumns(testTableName); // Assert - var dateTimeColumn1 = columns.Single(x => x.Name == dateTimeColumnName1); - var dateTimeColumn2 = columns.Single(x => x.Name == dateTimeColumnName2); - var decimalColumn1 = columns.Single(x => x.Name == decimalColumnName1); - var guidColumn1 = columns.Single(x => x.Name == guidColumnName1); - var booleanColumn1 = columns.Single(x => x.Name == booleanColumnName1); - var int32Column1 = columns.Single(x => x.Name == int32ColumnName1); - var int64Column1 = columns.Single(x => x.Name == int64ColumnName1); - var int64Column2 = columns.Single(x => x.Name == int64ColumnName2); - var stringColumn1 = columns.Single(x => x.Name == stringColumnName1); - var binarycolumn1 = columns.Single(x => x.Name == binaryColumnName1); - var doubleColumn1 = columns.Single(x => x.Name == doubleColumnName1); var intervalColumn1 = columns.Single(x => x.Name == intervalColumnName1); var intervalColumn2 = columns.Single(x => x.Name == intervalColumnName2); - Assert.That(dateTimeColumn1.DefaultValue, Is.EqualTo(dateTimeDefaultValue)); - Assert.That(dateTimeColumn2.DefaultValue, Is.EqualTo(dateTimeDefaultValue)); - Assert.That(decimalColumn1.DefaultValue, Is.EqualTo(DecimalDefaultValue)); - Assert.That(guidColumn1.DefaultValue, Is.EqualTo(guidDefaultValue)); - Assert.That(booleanColumn1.DefaultValue, Is.True); - Assert.That(int32Column1.DefaultValue, Is.EqualTo(43)); - Assert.That(int64Column1.DefaultValue, Is.EqualTo(88)); - Assert.That(stringColumn1.DefaultValue, Is.EqualTo("Hello")); - Assert.That(binarycolumn1.DefaultValue, Is.EqualTo(new byte[] { 12, 32, 34 })); - Assert.That(doubleColumn1.DefaultValue, Is.EqualTo(84.874596565)); Assert.That(intervalColumn1.DefaultValue, Is.EqualTo(new TimeSpan(100000, 3, 4, 5, 666))); Assert.That(intervalColumn2.DefaultValue, Is.EqualTo(new TimeSpan(0, 0, 0, 0, 666))); } diff --git a/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProviderGenericTests.cs b/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProviderGenericTests.cs index af56f8cd..3b3782a8 100644 --- a/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProviderGenericTests.cs +++ b/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProviderGenericTests.cs @@ -1,9 +1,10 @@ using DotNetProjects.Migrator.Providers.Impl.SQLite; +using Migrator.Tests.Providers.Base; using Migrator.Tests.Settings; using Migrator.Tests.Settings.Config; using NUnit.Framework; -namespace Migrator.Tests.Providers.SQLite.Base; +namespace Migrator.Tests.Providers.SQLite; [TestFixture] [Category("SQLite")] diff --git a/src/Migrator/Providers/Dialect.cs b/src/Migrator/Providers/Dialect.cs index ac15af49..eaad589a 100644 --- a/src/Migrator/Providers/Dialect.cs +++ b/src/Migrator/Providers/Dialect.cs @@ -366,7 +366,7 @@ public virtual string Default(object defaultValue) else if (defaultValue is byte[] byteArray) { var convertedString = BitConverter.ToString(byteArray).Replace("-", "").ToLower(); - defaultValue = $"'\\x{convertedString}'"; + defaultValue = $"0x{convertedString}"; } else if (defaultValue is double doubleValue) { diff --git a/src/Migrator/Providers/Impl/PostgreSQL/PostgreSQLDialect.cs b/src/Migrator/Providers/Impl/PostgreSQL/PostgreSQLDialect.cs index f536745e..348ef868 100644 --- a/src/Migrator/Providers/Impl/PostgreSQL/PostgreSQLDialect.cs +++ b/src/Migrator/Providers/Impl/PostgreSQL/PostgreSQLDialect.cs @@ -121,6 +121,11 @@ public override string Default(object defaultValue) return $"DEFAULT '{intervalPostgreNotation}'"; } + else if (defaultValue is byte[] byteArray) + { + var convertedString = BitConverter.ToString(byteArray).Replace("-", "").ToLower(); + return $"'\\x{convertedString}'"; + } return base.Default(defaultValue); } diff --git a/src/Migrator/Providers/Impl/SqlServer/SqlServerTransformationProvider.cs b/src/Migrator/Providers/Impl/SqlServer/SqlServerTransformationProvider.cs index 3cb2568d..0a880890 100644 --- a/src/Migrator/Providers/Impl/SqlServer/SqlServerTransformationProvider.cs +++ b/src/Migrator/Providers/Impl/SqlServer/SqlServerTransformationProvider.cs @@ -321,7 +321,7 @@ public override Column[] GetColumns(string table) var pkColumns = new List(); try { - pkColumns = this.ExecuteStringQuery("SELECT cu.COLUMN_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE cu WHERE EXISTS ( SELECT tc.* FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc WHERE tc.TABLE_NAME = '{0}' AND tc.CONSTRAINT_TYPE = 'PRIMARY KEY' AND tc.CONSTRAINT_NAME = cu.CONSTRAINT_NAME )", table); + pkColumns = ExecuteStringQuery("SELECT cu.COLUMN_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE cu WHERE EXISTS ( SELECT tc.* FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc WHERE tc.TABLE_NAME = '{0}' AND tc.CONSTRAINT_TYPE = 'PRIMARY KEY' AND tc.CONSTRAINT_NAME = cu.CONSTRAINT_NAME )", table); } catch (Exception) { } @@ -329,7 +329,7 @@ public override Column[] GetColumns(string table) var idtColumns = new List(); try { - idtColumns = this.ExecuteStringQuery(" select COLUMN_NAME from INFORMATION_SCHEMA.COLUMNS where TABLE_SCHEMA = '{1}' and TABLE_NAME = '{0}' and COLUMNPROPERTY(object_id(TABLE_NAME), COLUMN_NAME, 'IsIdentity') = 1", table, schema); + idtColumns = ExecuteStringQuery("SELECT COLUMN_NAME from INFORMATION_SCHEMA.COLUMNS where TABLE_SCHEMA = '{1}' and TABLE_NAME = '{0}' and COLUMNPROPERTY(object_id(TABLE_NAME), COLUMN_NAME, 'IsIdentity') = 1", table, schema); } catch (Exception) { } @@ -357,15 +357,18 @@ public override Column[] GetColumns(string table) var nullableStr = reader.GetString(1); var isNullable = nullableStr == "YES"; + if (!reader.IsDBNull(2)) { var type = reader.GetString(2); column.Type = Dialect.GetDbTypeFromString(type); } + if (!reader.IsDBNull(3)) { column.Size = reader.GetInt32(3); } + if (!reader.IsDBNull(4)) { column.DefaultValue = reader.GetValue(4); @@ -400,7 +403,7 @@ public override Column[] GetColumns(string table) if (column.DefaultValue is string defValCv && defValCv.StartsWith("CONVERT(")) { var dt = defValCv.Substring((defValCv.IndexOf("'") + 1), defValCv.IndexOf("'", defValCv.IndexOf("'") + 1) - defValCv.IndexOf("'") - 1); - var d = DateTime.ParseExact(dt, "yyyy-MM-dd HH:mm:ss.fff", CultureInfo.InvariantCulture); + var d = DateTime.ParseExact(dt, "yyyy-MM-dd HH:mm:ss.fff", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal); column.DefaultValue = d; } else if (column.DefaultValue is string defVal) @@ -411,9 +414,13 @@ public override Column[] GetColumns(string table) dt = defVal.Substring(1, defVal.Length - 2); } - var d = DateTime.ParseExact(dt, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture); + var d = DateTime.ParseExact(dt, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal); column.DefaultValue = d; } + else + { + throw new NotImplementedException($"Cannot interpret {column.DefaultValue} in column '{column.Name}' unexpected pattern."); + } } else if (column.Type == DbType.Guid) { From bac2e3f0a76e67091a6760a093d4acc23e047fbf Mon Sep 17 00:00:00 2001 From: JaBistDuNarrisch Date: Wed, 13 Aug 2025 08:47:08 +0200 Subject: [PATCH 02/27] Added SQLite begin transaction in base test class --- .../Base/TransformationProviderBase.cs | 21 ++++++- .../Base/TransformationProviderSimpleBase.cs | 4 +- .../OracleTransformationProviderTestBase.cs | 2 +- ...racleTransformationProviderGenericTests.cs | 40 +++++++++++++ .../OracleTransformationProviderTest.cs | 60 ------------------- 5 files changed, 60 insertions(+), 67 deletions(-) create mode 100644 src/Migrator.Tests/Providers/OracleProvider/OracleTransformationProviderGenericTests.cs delete mode 100644 src/Migrator.Tests/Providers/OracleProvider/OracleTransformationProviderTest.cs diff --git a/src/Migrator.Tests/Providers/Base/TransformationProviderBase.cs b/src/Migrator.Tests/Providers/Base/TransformationProviderBase.cs index b929bd2c..43545504 100644 --- a/src/Migrator.Tests/Providers/Base/TransformationProviderBase.cs +++ b/src/Migrator.Tests/Providers/Base/TransformationProviderBase.cs @@ -6,6 +6,7 @@ using DotNetProjects.Migrator.Framework; using DotNetProjects.Migrator.Providers; using DotNetProjects.Migrator.Providers.Impl.Oracle; +using DotNetProjects.Migrator.Providers.Impl.SQLite; using DryIoc; using Migrator.Tests.Database; using Migrator.Tests.Database.Interfaces; @@ -17,11 +18,13 @@ namespace Migrator.Tests.Providers.Base; /// -/// Base class for Provider tests for all non-constraint oriented tests. +/// Base class for provider tests. /// -public abstract class TransformationProviderBase : TransformationProviderSimpleBase +public abstract class TransformationProviderBase { - protected async Task StartOracleProvider() + protected ITransformationProvider Provider; + + protected async Task StartOracleTransactionAsync() { using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10)); var configReader = new ConfigurationReader(); @@ -47,4 +50,16 @@ protected async Task StartOracleProvider() Provider.BeginTransaction(); } + + protected async Task BeginSQLiteTransactionAsync() + { + var configReader = new ConfigurationReader(); + var connectionString = configReader.GetDatabaseConnectionConfigById(DatabaseConnectionConfigIds.SQLiteId) + .ConnectionString; + + Provider = new SQLiteTransformationProvider(new SQLiteDialect(), connectionString, "default", null); + Provider.BeginTransaction(); + + await Task.CompletedTask; + } } diff --git a/src/Migrator.Tests/Providers/Base/TransformationProviderSimpleBase.cs b/src/Migrator.Tests/Providers/Base/TransformationProviderSimpleBase.cs index 7a599de4..0f142c64 100644 --- a/src/Migrator.Tests/Providers/Base/TransformationProviderSimpleBase.cs +++ b/src/Migrator.Tests/Providers/Base/TransformationProviderSimpleBase.cs @@ -5,10 +5,8 @@ namespace Migrator.Tests.Providers.Base; -public abstract class TransformationProviderSimpleBase +public abstract class TransformationProviderSimpleBase : TransformationProviderBase { - protected ITransformationProvider Provider; - [TearDown] public virtual void TearDown() { diff --git a/src/Migrator.Tests/Providers/OracleProvider/Base/OracleTransformationProviderTestBase.cs b/src/Migrator.Tests/Providers/OracleProvider/Base/OracleTransformationProviderTestBase.cs index e1d7e65e..55d26af2 100644 --- a/src/Migrator.Tests/Providers/OracleProvider/Base/OracleTransformationProviderTestBase.cs +++ b/src/Migrator.Tests/Providers/OracleProvider/Base/OracleTransformationProviderTestBase.cs @@ -19,7 +19,7 @@ public class OracleTransformationProviderTestBase : TransformationProviderSimple [SetUp] public async Task SetUpAsync() { - + await StartOracleTransactionAsync(); AddDefaultTable(); } diff --git a/src/Migrator.Tests/Providers/OracleProvider/OracleTransformationProviderGenericTests.cs b/src/Migrator.Tests/Providers/OracleProvider/OracleTransformationProviderGenericTests.cs new file mode 100644 index 00000000..0e4f69a2 --- /dev/null +++ b/src/Migrator.Tests/Providers/OracleProvider/OracleTransformationProviderGenericTests.cs @@ -0,0 +1,40 @@ +using System; +using System.Data; +using System.Threading; +using System.Threading.Tasks; +using DotNetProjects.Migrator.Framework; +using DotNetProjects.Migrator.Providers; +using DotNetProjects.Migrator.Providers.Impl.Oracle; +using DryIoc; +using Migrator.Tests.Database; +using Migrator.Tests.Database.Interfaces; +using Migrator.Tests.Providers.Generic; +using Migrator.Tests.Settings; +using Migrator.Tests.Settings.Config; +using Migrator.Tests.Settings.Models; +using NUnit.Framework; + +namespace Migrator.Tests.Providers.OracleProvider; + +[TestFixture] +[Category("Oracle")] +public class OracleTransformationProviderGenericTests : TransformationProviderGenericMiscConstraintBase +{ + [SetUp] + public async Task SetUpAsync() + { + await StartOracleTransactionAsync(); + + AddDefaultTable(); + } + + [Test] + public void ChangeColumn_FromNotNullToNotNull() + { + Provider.ExecuteNonQuery("DELETE FROM TestTwo"); + Provider.ChangeColumn("TestTwo", new Column("TestId", DbType.String, 50, ColumnProperty.Null)); + Provider.Insert("TestTwo", ["Id", "TestId"], [3, "Not an Int val."]); + Provider.ChangeColumn("TestTwo", new Column("TestId", DbType.String, 50, ColumnProperty.NotNull)); + Provider.ChangeColumn("TestTwo", new Column("TestId", DbType.String, 50, ColumnProperty.NotNull)); + } +} diff --git a/src/Migrator.Tests/Providers/OracleProvider/OracleTransformationProviderTest.cs b/src/Migrator.Tests/Providers/OracleProvider/OracleTransformationProviderTest.cs deleted file mode 100644 index 1a582756..00000000 --- a/src/Migrator.Tests/Providers/OracleProvider/OracleTransformationProviderTest.cs +++ /dev/null @@ -1,60 +0,0 @@ -using System; -using System.Data; -using System.Threading; -using System.Threading.Tasks; -using DotNetProjects.Migrator.Framework; -using DotNetProjects.Migrator.Providers; -using DotNetProjects.Migrator.Providers.Impl.Oracle; -using DryIoc; -using Migrator.Tests.Database; -using Migrator.Tests.Database.Interfaces; -using Migrator.Tests.Settings; -using Migrator.Tests.Settings.Config; -using Migrator.Tests.Settings.Models; -using NUnit.Framework; - -namespace Migrator.Tests.Providers.OracleProvider; - -[TestFixture] -[Category("Oracle")] -public class OracleTransformationProviderTest : TransformationProviderConstraintBase -{ - [SetUp] - public async Task SetUpAsync() - { - using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10)); - var configReader = new ConfigurationReader(); - - var databaseConnectionConfig = configReader.GetDatabaseConnectionConfigById(DatabaseConnectionConfigIds.OracleId); - - var connectionString = databaseConnectionConfig?.ConnectionString; - - if (string.IsNullOrEmpty(connectionString)) - { - throw new IgnoreException($"No Oracle {nameof(DatabaseConnectionConfig.ConnectionString)} is set."); - } - - DbProviderFactories.RegisterFactory("Oracle.ManagedDataAccess.Client", () => Oracle.ManagedDataAccess.Client.OracleClientFactory.Instance); - - using var container = new Container(); - container.RegisterDatabaseIntegrationTestService(); - var databaseIntegrationTestServiceFactory = container.Resolve(); - var oracleIntegrationTestService = databaseIntegrationTestServiceFactory.Create(DatabaseProviderType.Oracle); - var databaseInfo = await oracleIntegrationTestService.CreateTestDatabaseAsync(databaseConnectionConfig, cts.Token); - - Provider = new OracleTransformationProvider(new OracleDialect(), databaseInfo.DatabaseConnectionConfig.ConnectionString, null, "default", "Oracle.ManagedDataAccess.Client"); - Provider.BeginTransaction(); - - AddDefaultTable(); - } - - [Test] - public void ChangeColumn_FromNotNullToNotNull() - { - Provider.ExecuteNonQuery("DELETE FROM TestTwo"); - Provider.ChangeColumn("TestTwo", new Column("TestId", DbType.String, 50, ColumnProperty.Null)); - Provider.Insert("TestTwo", ["Id", "TestId"], [3, "Not an Int val."]); - Provider.ChangeColumn("TestTwo", new Column("TestId", DbType.String, 50, ColumnProperty.NotNull)); - Provider.ChangeColumn("TestTwo", new Column("TestId", DbType.String, 50, ColumnProperty.NotNull)); - } -} From c3d24ad2094bd3fbf6430e3e9f675e02a5c91fbb Mon Sep 17 00:00:00 2001 From: JaBistDuNarrisch Date: Wed, 13 Aug 2025 09:15:41 +0200 Subject: [PATCH 03/27] Oracle, SQL Server, SQLite and Postgre SQL Begin Transaction moved to base class --- .../Base/TransformationProviderBase.cs | 51 +++++++++++++++++-- .../OracleTransformationProviderTestBase.cs | 2 +- ...racleTransformationProviderGenericTests.cs | 2 +- ...reSQLTransformationProviderGenericTests.cs | 18 +++++++ .../PostgreSQLTransformationProviderTest.cs | 32 ------------ ...erverTransformationProviderGenericTests.cs | 25 ++------- .../SQLiteTransformationProviderTestBase.cs | 5 +- ...QLiteTransformationProviderGenericTests.cs | 17 ++----- ...ransformationProvider_RenameColumnTests.cs | 5 +- 9 files changed, 81 insertions(+), 76 deletions(-) create mode 100644 src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProviderGenericTests.cs delete mode 100644 src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProviderTest.cs diff --git a/src/Migrator.Tests/Providers/Base/TransformationProviderBase.cs b/src/Migrator.Tests/Providers/Base/TransformationProviderBase.cs index 43545504..7af47db7 100644 --- a/src/Migrator.Tests/Providers/Base/TransformationProviderBase.cs +++ b/src/Migrator.Tests/Providers/Base/TransformationProviderBase.cs @@ -1,12 +1,12 @@ using System; -using System.Data; -using System.Linq; using System.Threading; using System.Threading.Tasks; using DotNetProjects.Migrator.Framework; using DotNetProjects.Migrator.Providers; using DotNetProjects.Migrator.Providers.Impl.Oracle; +using DotNetProjects.Migrator.Providers.Impl.PostgreSQL; using DotNetProjects.Migrator.Providers.Impl.SQLite; +using DotNetProjects.Migrator.Providers.Impl.SqlServer; using DryIoc; using Migrator.Tests.Database; using Migrator.Tests.Database.Interfaces; @@ -24,7 +24,7 @@ public abstract class TransformationProviderBase { protected ITransformationProvider Provider; - protected async Task StartOracleTransactionAsync() + protected async Task BeginOracleTransactionAsync() { using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10)); var configReader = new ConfigurationReader(); @@ -51,6 +51,25 @@ protected async Task StartOracleTransactionAsync() Provider.BeginTransaction(); } + protected async Task BeginPostgreSQLTransactionAsync() + { + var configReader = new ConfigurationReader(); + var connectionString = configReader.GetDatabaseConnectionConfigById(DatabaseConnectionConfigIds.PostgreSQL) + ?.ConnectionString; + + if (string.IsNullOrEmpty(connectionString)) + { + throw new IgnoreException("No Postgre ConnectionString is Set."); + } + + DbProviderFactories.RegisterFactory("Npgsql", () => Npgsql.NpgsqlFactory.Instance); + + Provider = new PostgreSQLTransformationProvider(new PostgreSQLDialect(), connectionString, null, "default", "Npgsql"); + Provider.BeginTransaction(); + + await Task.CompletedTask; + } + protected async Task BeginSQLiteTransactionAsync() { var configReader = new ConfigurationReader(); @@ -62,4 +81,30 @@ protected async Task BeginSQLiteTransactionAsync() await Task.CompletedTask; } + + protected async Task BeginSQLServerTransactionAsync() + { + var configReader = new ConfigurationReader(); + + var databaseConnectionConfig = configReader.GetDatabaseConnectionConfigById(DatabaseConnectionConfigIds.SQLServerId); + + var connectionString = databaseConnectionConfig?.ConnectionString; + + if (string.IsNullOrEmpty(connectionString)) + { + throw new IgnoreException($"No SQL Server {nameof(DatabaseConnectionConfig.ConnectionString)} is set."); + } + + DbProviderFactories.RegisterFactory("Microsoft.Data.SqlClient", () => Microsoft.Data.SqlClient.SqlClientFactory.Instance); + + using var container = new Container(); + container.RegisterDatabaseIntegrationTestService(); + var databaseIntegrationTestServiceFactory = container.Resolve(); + var sqlServerIntegrationTestService = databaseIntegrationTestServiceFactory.Create(DatabaseProviderType.SQLServer); + var databaseInfo = await sqlServerIntegrationTestService.CreateTestDatabaseAsync(databaseConnectionConfig, CancellationToken.None); + + Provider = new SqlServerTransformationProvider(new SqlServerDialect(), databaseInfo.DatabaseConnectionConfig.ConnectionString, "dbo", "default", "Microsoft.Data.SqlClient"); + + Provider.BeginTransaction(); + } } diff --git a/src/Migrator.Tests/Providers/OracleProvider/Base/OracleTransformationProviderTestBase.cs b/src/Migrator.Tests/Providers/OracleProvider/Base/OracleTransformationProviderTestBase.cs index 55d26af2..8839dc37 100644 --- a/src/Migrator.Tests/Providers/OracleProvider/Base/OracleTransformationProviderTestBase.cs +++ b/src/Migrator.Tests/Providers/OracleProvider/Base/OracleTransformationProviderTestBase.cs @@ -19,7 +19,7 @@ public class OracleTransformationProviderTestBase : TransformationProviderSimple [SetUp] public async Task SetUpAsync() { - await StartOracleTransactionAsync(); + await BeginOracleTransactionAsync(); AddDefaultTable(); } diff --git a/src/Migrator.Tests/Providers/OracleProvider/OracleTransformationProviderGenericTests.cs b/src/Migrator.Tests/Providers/OracleProvider/OracleTransformationProviderGenericTests.cs index 0e4f69a2..d7ffcdce 100644 --- a/src/Migrator.Tests/Providers/OracleProvider/OracleTransformationProviderGenericTests.cs +++ b/src/Migrator.Tests/Providers/OracleProvider/OracleTransformationProviderGenericTests.cs @@ -23,7 +23,7 @@ public class OracleTransformationProviderGenericTests : TransformationProviderGe [SetUp] public async Task SetUpAsync() { - await StartOracleTransactionAsync(); + await BeginOracleTransactionAsync(); AddDefaultTable(); } diff --git a/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProviderGenericTests.cs b/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProviderGenericTests.cs new file mode 100644 index 00000000..5fce7dfd --- /dev/null +++ b/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProviderGenericTests.cs @@ -0,0 +1,18 @@ +using System.Threading.Tasks; +using Migrator.Tests.Providers.Generic; +using NUnit.Framework; + +namespace Migrator.Tests.Providers.PostgreSQL; + +[TestFixture] +[Category("Postgre")] +public class PostgreSQLTransformationProviderGenericTests : TransformationProviderGenericMiscConstraintBase +{ + [SetUp] + public async Task SetUpAsync() + { + await BeginPostgreSQLTransactionAsync(); + + AddDefaultTable(); + } +} diff --git a/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProviderTest.cs b/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProviderTest.cs deleted file mode 100644 index f24a1289..00000000 --- a/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProviderTest.cs +++ /dev/null @@ -1,32 +0,0 @@ -using DotNetProjects.Migrator.Providers; -using DotNetProjects.Migrator.Providers.Impl.PostgreSQL; -using Migrator.Tests.Settings; -using Migrator.Tests.Settings.Config; -using NUnit.Framework; - -namespace Migrator.Tests.Providers.PostgreSQL; - -[TestFixture] -[Category("Postgre")] -public class PostgreSQLTransformationProviderTest : TransformationProviderConstraintBase -{ - [SetUp] - public void SetUp() - { - var configReader = new ConfigurationReader(); - var connectionString = configReader.GetDatabaseConnectionConfigById(DatabaseConnectionConfigIds.PostgreSQL) - ?.ConnectionString; - - if (string.IsNullOrEmpty(connectionString)) - { - throw new IgnoreException("No Postgre ConnectionString is Set."); - } - - DbProviderFactories.RegisterFactory("Npgsql", () => Npgsql.NpgsqlFactory.Instance); - - Provider = new PostgreSQLTransformationProvider(new PostgreSQLDialect(), connectionString, null, "default", "Npgsql"); - Provider.BeginTransaction(); - - AddDefaultTable(); - } -} diff --git a/src/Migrator.Tests/Providers/SQLServer/SqlServerTransformationProviderGenericTests.cs b/src/Migrator.Tests/Providers/SQLServer/SqlServerTransformationProviderGenericTests.cs index e574d275..6b483edc 100644 --- a/src/Migrator.Tests/Providers/SQLServer/SqlServerTransformationProviderGenericTests.cs +++ b/src/Migrator.Tests/Providers/SQLServer/SqlServerTransformationProviderGenericTests.cs @@ -7,6 +7,7 @@ using DryIoc; using Migrator.Tests.Database; using Migrator.Tests.Database.Interfaces; +using Migrator.Tests.Providers.Generic; using Migrator.Tests.Settings; using Migrator.Tests.Settings.Config; using Migrator.Tests.Settings.Models; @@ -16,32 +17,12 @@ namespace Migrator.Tests.Providers.SQLServer; [TestFixture] [Category("SqlServer")] -public class SqlServerTransformationProviderGenericTests : TransformationProviderConstraintBase +public class SqlServerTransformationProviderGenericTests : TransformationProviderGenericMiscConstraintBase { [SetUp] public async Task SetUpAsync() { - var configReader = new ConfigurationReader(); - - var databaseConnectionConfig = configReader.GetDatabaseConnectionConfigById(DatabaseConnectionConfigIds.SQLServerId); - - var connectionString = databaseConnectionConfig?.ConnectionString; - - if (string.IsNullOrEmpty(connectionString)) - { - throw new IgnoreException($"No SQL Server {nameof(DatabaseConnectionConfig.ConnectionString)} is set."); - } - - DbProviderFactories.RegisterFactory("Microsoft.Data.SqlClient", () => Microsoft.Data.SqlClient.SqlClientFactory.Instance); - - using var container = new Container(); - container.RegisterDatabaseIntegrationTestService(); - var databaseIntegrationTestServiceFactory = container.Resolve(); - var sqlServerIntegrationTestService = databaseIntegrationTestServiceFactory.Create(DatabaseProviderType.SQLServer); - var databaseInfo = await sqlServerIntegrationTestService.CreateTestDatabaseAsync(databaseConnectionConfig, CancellationToken.None); - - Provider = new SqlServerTransformationProvider(new SqlServerDialect(), databaseInfo.DatabaseConnectionConfig.ConnectionString, "dbo", "default", "Microsoft.Data.SqlClient"); - Provider.BeginTransaction(); + dfdfg AddDefaultTable(); } diff --git a/src/Migrator.Tests/Providers/SQLite/Base/SQLiteTransformationProviderTestBase.cs b/src/Migrator.Tests/Providers/SQLite/Base/SQLiteTransformationProviderTestBase.cs index 69cd6a1a..3c6ffa6a 100644 --- a/src/Migrator.Tests/Providers/SQLite/Base/SQLiteTransformationProviderTestBase.cs +++ b/src/Migrator.Tests/Providers/SQLite/Base/SQLiteTransformationProviderTestBase.cs @@ -1,3 +1,4 @@ +using System.Threading.Tasks; using DotNetProjects.Migrator.Providers.Impl.SQLite; using Migrator.Tests.Providers.Base; using Migrator.Tests.Settings; @@ -11,7 +12,7 @@ namespace Migrator.Tests.Providers.SQLite.Base; public abstract class SQLiteTransformationProviderTestBase : TransformationProviderSimpleBase { [SetUp] - public void SetUp() + public async Task SetUpAsync() { var configReader = new ConfigurationReader(); var connectionString = configReader.GetDatabaseConnectionConfigById(DatabaseConnectionConfigIds.SQLiteId) @@ -20,6 +21,6 @@ public void SetUp() Provider = new SQLiteTransformationProvider(new SQLiteDialect(), connectionString, "default", null); Provider.BeginTransaction(); - AddDefaultTable(); + await Task.CompletedTask; } } diff --git a/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProviderGenericTests.cs b/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProviderGenericTests.cs index 3b3782a8..2a7ba7ba 100644 --- a/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProviderGenericTests.cs +++ b/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProviderGenericTests.cs @@ -1,24 +1,17 @@ -using DotNetProjects.Migrator.Providers.Impl.SQLite; -using Migrator.Tests.Providers.Base; -using Migrator.Tests.Settings; -using Migrator.Tests.Settings.Config; +using System.Threading.Tasks; +using Migrator.Tests.Providers.Generic; using NUnit.Framework; namespace Migrator.Tests.Providers.SQLite; [TestFixture] [Category("SQLite")] -public class SQLiteTransformationProviderGenericTests : TransformationProviderBase +public class SQLiteTransformationProviderGenericTests : TransformationProviderGenericMiscConstraintBase { [SetUp] - public void SetUp() + public async Task SetUpAsync() { - var configReader = new ConfigurationReader(); - var connectionString = configReader.GetDatabaseConnectionConfigById(DatabaseConnectionConfigIds.SQLiteId) - .ConnectionString; - - Provider = new SQLiteTransformationProvider(new SQLiteDialect(), connectionString, "default", null); - Provider.BeginTransaction(); + await BeginSQLiteTransactionAsync(); AddDefaultTable(); } diff --git a/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_RenameColumnTests.cs b/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_RenameColumnTests.cs index 8821031d..a69617ec 100644 --- a/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_RenameColumnTests.cs +++ b/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_RenameColumnTests.cs @@ -1,16 +1,15 @@ -using System; using System.Data; using System.Linq; using DotNetProjects.Migrator.Framework; using DotNetProjects.Migrator.Providers.Impl.SQLite; -using Migrator.Tests.Providers.SQLite.Base; +using Migrator.Tests.Providers.Base; using NUnit.Framework; namespace Migrator.Tests.Providers.SQLite; [TestFixture] [Category("SQLite")] -public class SQLiteTransformationProvider_RenameColumnTests : SQLiteTransformationProviderTestBase +public class SQLiteTransformationProvider_RenameColumnTests : TransformationProviderBase { [Test] public void RenameColumn_HavingASingleForeignKeyPointingToTheTargetColumn_SingleColumnForeignKeyIsRemoved() From 69afb8c12c0d6e320351b9156fbfd342696273fb Mon Sep 17 00:00:00 2001 From: JaBistDuNarrisch Date: Wed, 13 Aug 2025 10:00:20 +0200 Subject: [PATCH 04/27] GetColumns tests --- .../Base/TransformationProviderBase.cs | 35 ++++++++ .../Base/TransformationProviderSimpleBase.cs | 35 -------- .../TransformationProviderGenericMiscTests.cs | 68 --------------- ...rmationProvider_GetColumns_GenericTests.cs | 82 +++++++++++++++++++ .../OracleTransformationProvider_asdf.cs | 16 ++++ ...ionProvider_GetColumnContent_SizeTests.cs} | 2 +- ...nProvider_GetColumns_DefaultValueTests.cs} | 2 +- ...TransformationProvider_GetColumns_Tests.cs | 16 ++++ ...SQLServerTransformationProviderTestBase.cs | 30 +------ ...rTransformationProvider_GetColumnsTests.cs | 16 ++++ ...erverTransformationProviderGenericTests.cs | 10 +-- ...eTransformationProvider_GetColumnsTests.cs | 11 ++- 12 files changed, 178 insertions(+), 145 deletions(-) create mode 100644 src/Migrator.Tests/Providers/Generic/TransformationProvider_GetColumns_GenericTests.cs create mode 100644 src/Migrator.Tests/Providers/OracleProvider/OracleTransformationProvider_asdf.cs rename src/Migrator.Tests/Providers/PostgreSQL/{PostgreSQLTransformationProvider_GetColumnContentSizeTests.cs => PostgreSQLTransformationProvider_GetColumnContent_SizeTests.cs} (96%) rename src/Migrator.Tests/Providers/PostgreSQL/{PostgreSQLTransformationProvider_GetColumnsDefaultValueTests.cs => PostgreSQLTransformationProvider_GetColumns_DefaultValueTests.cs} (96%) create mode 100644 src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_GetColumns_Tests.cs create mode 100644 src/Migrator.Tests/Providers/SQLServer/SQLServerTransformationProvider_GetColumnsTests.cs diff --git a/src/Migrator.Tests/Providers/Base/TransformationProviderBase.cs b/src/Migrator.Tests/Providers/Base/TransformationProviderBase.cs index 7af47db7..9dbb2ee4 100644 --- a/src/Migrator.Tests/Providers/Base/TransformationProviderBase.cs +++ b/src/Migrator.Tests/Providers/Base/TransformationProviderBase.cs @@ -22,6 +22,41 @@ namespace Migrator.Tests.Providers.Base; /// public abstract class TransformationProviderBase { + [TearDown] + public virtual void TearDown() + { + DropTestTables(); + + Provider?.Rollback(); + } + + protected void DropTestTables() + { + // Because MySql doesn't support schema transaction + // we got to remove the tables manually... sad... + try + { + Provider.RemoveTable("TestTwo"); + } + catch (Exception) + { + } + try + { + Provider.RemoveTable("Test"); + } + catch (Exception) + { + } + try + { + Provider.RemoveTable("SchemaInfo"); + } + catch (Exception) + { + } + } + protected ITransformationProvider Provider; protected async Task BeginOracleTransactionAsync() diff --git a/src/Migrator.Tests/Providers/Base/TransformationProviderSimpleBase.cs b/src/Migrator.Tests/Providers/Base/TransformationProviderSimpleBase.cs index 0f142c64..82a93a79 100644 --- a/src/Migrator.Tests/Providers/Base/TransformationProviderSimpleBase.cs +++ b/src/Migrator.Tests/Providers/Base/TransformationProviderSimpleBase.cs @@ -7,41 +7,6 @@ namespace Migrator.Tests.Providers.Base; public abstract class TransformationProviderSimpleBase : TransformationProviderBase { - [TearDown] - public virtual void TearDown() - { - DropTestTables(); - - Provider?.Rollback(); - } - - protected void DropTestTables() - { - // Because MySql doesn't support schema transaction - // we got to remove the tables manually... sad... - try - { - Provider.RemoveTable("TestTwo"); - } - catch (Exception) - { - } - try - { - Provider.RemoveTable("Test"); - } - catch (Exception) - { - } - try - { - Provider.RemoveTable("SchemaInfo"); - } - catch (Exception) - { - } - } - public void AddDefaultTable() { Provider.AddTable("TestTwo", diff --git a/src/Migrator.Tests/Providers/Generic/TransformationProviderGenericMiscTests.cs b/src/Migrator.Tests/Providers/Generic/TransformationProviderGenericMiscTests.cs index 4b5a200c..5647cba2 100644 --- a/src/Migrator.Tests/Providers/Generic/TransformationProviderGenericMiscTests.cs +++ b/src/Migrator.Tests/Providers/Generic/TransformationProviderGenericMiscTests.cs @@ -12,74 +12,6 @@ namespace Migrator.Tests.Providers.Generic; /// public abstract class TransformationProviderGenericMiscTests : TransformationProviderSimpleBase { - [Test] - public void GetColumns_DefaultValues_Succeeds() - { - // Arrange - var dateTimeDefaultValue = new DateTime(2000, 1, 2, 3, 4, 5, DateTimeKind.Utc); - var guidDefaultValue = Guid.NewGuid(); - var decimalDefaultValue = 14.56565m; - - const string testTableName = "MyDefaultTestTable"; - - const string dateTimeColumnName1 = "datetimecolumn1"; - const string dateTimeColumnName2 = "datetimecolumn2"; - const string decimalColumnName1 = "decimalcolumn"; - const string guidColumnName1 = "guidcolumn1"; - const string booleanColumnName1 = "booleancolumn1"; - const string int32ColumnName1 = "int32column1"; - const string int64ColumnName1 = "int64column1"; - const string int64ColumnName2 = "int64column2"; - const string stringColumnName1 = "stringcolumn1"; - const string binaryColumnName1 = "binarycolumn1"; - const string doubleColumnName1 = "doublecolumn1"; - - // Should be extended by remaining types - Provider.AddTable(testTableName, - new Column(dateTimeColumnName1, DbType.DateTime, dateTimeDefaultValue), - new Column(dateTimeColumnName2, DbType.DateTime2, dateTimeDefaultValue), - new Column(decimalColumnName1, DbType.Decimal, decimalDefaultValue), - new Column(guidColumnName1, DbType.Guid, guidDefaultValue), - - // other boolean default values are tested in another test - new Column(booleanColumnName1, DbType.Boolean, true), - - new Column(int32ColumnName1, DbType.Int32, defaultValue: 43), - new Column(int64ColumnName1, DbType.Int64, defaultValue: 88), - new Column(int64ColumnName2, DbType.Int64, defaultValue: 0), - new Column(stringColumnName1, DbType.String, defaultValue: "Hello"), - new Column(binaryColumnName1, DbType.Binary, defaultValue: new byte[] { 12, 32, 34 }), - new Column(doubleColumnName1, DbType.Double, defaultValue: 84.874596565) - ); - - // Act - var columns = Provider.GetColumns(testTableName); - - // Assert - var dateTimeColumn1 = columns.Single(x => x.Name == dateTimeColumnName1); - var dateTimeColumn2 = columns.Single(x => x.Name == dateTimeColumnName2); - var decimalColumn1 = columns.Single(x => x.Name == decimalColumnName1); - var guidColumn1 = columns.Single(x => x.Name == guidColumnName1); - var booleanColumn1 = columns.Single(x => x.Name == booleanColumnName1); - var int32Column1 = columns.Single(x => x.Name == int32ColumnName1); - var int64Column1 = columns.Single(x => x.Name == int64ColumnName1); - var int64Column2 = columns.Single(x => x.Name == int64ColumnName2); - var stringColumn1 = columns.Single(x => x.Name == stringColumnName1); - var binarycolumn1 = columns.Single(x => x.Name == binaryColumnName1); - var doubleColumn1 = columns.Single(x => x.Name == doubleColumnName1); - - Assert.That(dateTimeColumn1.DefaultValue, Is.EqualTo(dateTimeDefaultValue)); - Assert.That(dateTimeColumn2.DefaultValue, Is.EqualTo(dateTimeDefaultValue)); - Assert.That(decimalColumn1.DefaultValue, Is.EqualTo(decimalDefaultValue)); - Assert.That(guidColumn1.DefaultValue, Is.EqualTo(guidDefaultValue)); - Assert.That(booleanColumn1.DefaultValue, Is.True); - Assert.That(int32Column1.DefaultValue, Is.EqualTo(43)); - Assert.That(int64Column1.DefaultValue, Is.EqualTo(88)); - Assert.That(stringColumn1.DefaultValue, Is.EqualTo("Hello")); - Assert.That(binarycolumn1.DefaultValue, Is.EqualTo(new byte[] { 12, 32, 34 })); - Assert.That(doubleColumn1.DefaultValue, Is.EqualTo(84.874596565)); - } - [Test] public void TableExistsWorks() { diff --git a/src/Migrator.Tests/Providers/Generic/TransformationProvider_GetColumns_GenericTests.cs b/src/Migrator.Tests/Providers/Generic/TransformationProvider_GetColumns_GenericTests.cs new file mode 100644 index 00000000..8fb54927 --- /dev/null +++ b/src/Migrator.Tests/Providers/Generic/TransformationProvider_GetColumns_GenericTests.cs @@ -0,0 +1,82 @@ +using System; +using System.Data; +using System.Linq; +using DotNetProjects.Migrator.Framework; +using Migrator.Tests.Providers.Base; +using NUnit.Framework; + +namespace Migrator.Tests.Providers.Generic; + +/// +/// Base class for provider tests. +/// +public abstract class TransformationProvider_GetColumns_GenericTests : TransformationProviderBase +{ + [Test] + public void GetColumns_DefaultValues_Succeeds() + { + // Arrange + var dateTimeDefaultValue = new DateTime(2000, 1, 2, 3, 4, 5, DateTimeKind.Utc); + var guidDefaultValue = Guid.NewGuid(); + var decimalDefaultValue = 14.56565m; + + const string testTableName = "MyDefaultTestTable"; + + const string dateTimeColumnName1 = "datetimecolumn1"; + const string dateTimeColumnName2 = "datetimecolumn2"; + const string decimalColumnName1 = "decimalcolumn"; + const string guidColumnName1 = "guidcolumn1"; + const string booleanColumnName1 = "booleancolumn1"; + const string int32ColumnName1 = "int32column1"; + const string int64ColumnName1 = "int64column1"; + const string int64ColumnName2 = "int64column2"; + const string stringColumnName1 = "stringcolumn1"; + const string binaryColumnName1 = "binarycolumn1"; + const string doubleColumnName1 = "doublecolumn1"; + + // Should be extended by remaining types + Provider.AddTable(testTableName, + new Column(dateTimeColumnName1, DbType.DateTime, dateTimeDefaultValue), + new Column(dateTimeColumnName2, DbType.DateTime2, dateTimeDefaultValue), + new Column(decimalColumnName1, DbType.Decimal, decimalDefaultValue), + new Column(guidColumnName1, DbType.Guid, guidDefaultValue), + + // other boolean default values are tested in another test + new Column(booleanColumnName1, DbType.Boolean, true), + + new Column(int32ColumnName1, DbType.Int32, defaultValue: 43), + new Column(int64ColumnName1, DbType.Int64, defaultValue: 88), + new Column(int64ColumnName2, DbType.Int64, defaultValue: 0), + new Column(stringColumnName1, DbType.String, defaultValue: "Hello"), + new Column(binaryColumnName1, DbType.Binary, defaultValue: new byte[] { 12, 32, 34 }), + new Column(doubleColumnName1, DbType.Double, defaultValue: 84.874596565) + ); + + // Act + var columns = Provider.GetColumns(testTableName); + + // Assert + var dateTimeColumn1 = columns.Single(x => x.Name == dateTimeColumnName1); + var dateTimeColumn2 = columns.Single(x => x.Name == dateTimeColumnName2); + var decimalColumn1 = columns.Single(x => x.Name == decimalColumnName1); + var guidColumn1 = columns.Single(x => x.Name == guidColumnName1); + var booleanColumn1 = columns.Single(x => x.Name == booleanColumnName1); + var int32Column1 = columns.Single(x => x.Name == int32ColumnName1); + var int64Column1 = columns.Single(x => x.Name == int64ColumnName1); + var int64Column2 = columns.Single(x => x.Name == int64ColumnName2); + var stringColumn1 = columns.Single(x => x.Name == stringColumnName1); + var binarycolumn1 = columns.Single(x => x.Name == binaryColumnName1); + var doubleColumn1 = columns.Single(x => x.Name == doubleColumnName1); + + Assert.That(dateTimeColumn1.DefaultValue, Is.EqualTo(dateTimeDefaultValue)); + Assert.That(dateTimeColumn2.DefaultValue, Is.EqualTo(dateTimeDefaultValue)); + Assert.That(decimalColumn1.DefaultValue, Is.EqualTo(decimalDefaultValue)); + Assert.That(guidColumn1.DefaultValue, Is.EqualTo(guidDefaultValue)); + Assert.That(booleanColumn1.DefaultValue, Is.True); + Assert.That(int32Column1.DefaultValue, Is.EqualTo(43)); + Assert.That(int64Column1.DefaultValue, Is.EqualTo(88)); + Assert.That(stringColumn1.DefaultValue, Is.EqualTo("Hello")); + Assert.That(binarycolumn1.DefaultValue, Is.EqualTo(new byte[] { 12, 32, 34 })); + Assert.That(doubleColumn1.DefaultValue, Is.EqualTo(84.874596565)); + } +} diff --git a/src/Migrator.Tests/Providers/OracleProvider/OracleTransformationProvider_asdf.cs b/src/Migrator.Tests/Providers/OracleProvider/OracleTransformationProvider_asdf.cs new file mode 100644 index 00000000..990c6d9b --- /dev/null +++ b/src/Migrator.Tests/Providers/OracleProvider/OracleTransformationProvider_asdf.cs @@ -0,0 +1,16 @@ +using System.Threading.Tasks; +using Migrator.Tests.Providers.Generic; +using NUnit.Framework; + +namespace Migrator.Tests.Providers.OracleProvider; + +[TestFixture] +[Category("Oracle")] +public class OracleTransformationProvider_GetColumns_Tests : TransformationProvider_GetColumns_GenericTests +{ + [SetUp] + public async Task SetUpAsync() + { + await BeginOracleTransactionAsync(); + } +} diff --git a/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_GetColumnContentSizeTests.cs b/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_GetColumnContent_SizeTests.cs similarity index 96% rename from src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_GetColumnContentSizeTests.cs rename to src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_GetColumnContent_SizeTests.cs index d2a3701f..feed0dc9 100644 --- a/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_GetColumnContentSizeTests.cs +++ b/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_GetColumnContent_SizeTests.cs @@ -7,7 +7,7 @@ namespace Migrator.Tests.Providers.PostgreSQL; [TestFixture] [Category("Postgre")] -public class PostgreSQLTransformationProvider_GetColumnContentSizeTests : PostgreSQLTransformationProviderTestBase +public class PostgreSQLTransformationProvider_GetColumnContentSize_Tests : PostgreSQLTransformationProviderTestBase { [Test] public void GetColumnContentSize_UseStringColumn_MaxContentLengthIsCorrect() diff --git a/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_GetColumnsDefaultValueTests.cs b/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_GetColumns_DefaultValueTests.cs similarity index 96% rename from src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_GetColumnsDefaultValueTests.cs rename to src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_GetColumns_DefaultValueTests.cs index 0d76beda..283faf7e 100644 --- a/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_GetColumnsDefaultValueTests.cs +++ b/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_GetColumns_DefaultValueTests.cs @@ -9,7 +9,7 @@ namespace Migrator.Tests.Providers.PostgreSQL; [TestFixture] [Category("Postgre")] -public class PostgreSQLTransformationProvider_GetColumnsDefaultTypeTests : PostgreSQLTransformationProviderTestBase +public class PostgreSQLTransformationProvider_GetColumns_DefaultTypeTests : PostgreSQLTransformationProviderTestBase { /// /// More tests for GetColumns in diff --git a/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_GetColumns_Tests.cs b/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_GetColumns_Tests.cs new file mode 100644 index 00000000..5fa61ec0 --- /dev/null +++ b/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_GetColumns_Tests.cs @@ -0,0 +1,16 @@ +using System.Threading.Tasks; +using Migrator.Tests.Providers.Generic; +using NUnit.Framework; + +namespace Migrator.Tests.Providers.PostgreSQL; + +[TestFixture] +[Category("Postgre")] +public class PostgreSQLTransformationProvider_GetColumns_Tests : TransformationProvider_GetColumns_GenericTests +{ + [SetUp] + public async Task SetUpAsync() + { + await BeginPostgreSQLTransactionAsync(); + } +} diff --git a/src/Migrator.Tests/Providers/SQLServer/Base/SQLServerTransformationProviderTestBase.cs b/src/Migrator.Tests/Providers/SQLServer/Base/SQLServerTransformationProviderTestBase.cs index 7f9cbe32..2c76169a 100644 --- a/src/Migrator.Tests/Providers/SQLServer/Base/SQLServerTransformationProviderTestBase.cs +++ b/src/Migrator.Tests/Providers/SQLServer/Base/SQLServerTransformationProviderTestBase.cs @@ -1,14 +1,5 @@ -using System.Threading; using System.Threading.Tasks; -using DotNetProjects.Migrator.Providers; -using DotNetProjects.Migrator.Providers.Impl.SqlServer; -using DryIoc; -using Migrator.Tests.Database; -using Migrator.Tests.Database.Interfaces; using Migrator.Tests.Providers.Base; -using Migrator.Tests.Settings; -using Migrator.Tests.Settings.Config; -using Migrator.Tests.Settings.Models; using NUnit.Framework; namespace Migrator.Tests.Providers.SQLServer.Base; @@ -20,26 +11,7 @@ public abstract class SQLServerTransformationProviderTestBase : TransformationPr [SetUp] public async Task SetUpAsync() { - var configReader = new ConfigurationReader(); - - var databaseConnectionConfig = configReader.GetDatabaseConnectionConfigById(DatabaseConnectionConfigIds.SQLServerId); - - var connectionString = databaseConnectionConfig?.ConnectionString; - - if (string.IsNullOrEmpty(connectionString)) - { - throw new IgnoreException($"No SQL Server {nameof(DatabaseConnectionConfig.ConnectionString)} is set."); - } - - DbProviderFactories.RegisterFactory("Microsoft.Data.SqlClient", () => Microsoft.Data.SqlClient.SqlClientFactory.Instance); - - using var container = new Container(); - container.RegisterDatabaseIntegrationTestService(); - var databaseIntegrationTestServiceFactory = container.Resolve(); - var sqlServerIntegrationTestService = databaseIntegrationTestServiceFactory.Create(DatabaseProviderType.SQLServer); - var databaseInfo = await sqlServerIntegrationTestService.CreateTestDatabaseAsync(databaseConnectionConfig, CancellationToken.None); - - Provider = new SqlServerTransformationProvider(new SqlServerDialect(), databaseInfo.DatabaseConnectionConfig.ConnectionString, "dbo", "default", "Microsoft.Data.SqlClient"); + await BeginSQLServerTransactionAsync(); AddDefaultTable(); } diff --git a/src/Migrator.Tests/Providers/SQLServer/SQLServerTransformationProvider_GetColumnsTests.cs b/src/Migrator.Tests/Providers/SQLServer/SQLServerTransformationProvider_GetColumnsTests.cs new file mode 100644 index 00000000..77caea42 --- /dev/null +++ b/src/Migrator.Tests/Providers/SQLServer/SQLServerTransformationProvider_GetColumnsTests.cs @@ -0,0 +1,16 @@ +using System.Threading.Tasks; +using Migrator.Tests.Providers.Generic; +using NUnit.Framework; + +namespace Migrator.Tests.Providers.SQLServer; + +[TestFixture] +[Category("SqlServer")] +public class SQLServerTransformationProvider_GetColumnsTests : TransformationProvider_GetColumns_GenericTests +{ + [SetUp] + public async Task SetUpAsync() + { + await BeginSQLServerTransactionAsync(); + } +} diff --git a/src/Migrator.Tests/Providers/SQLServer/SqlServerTransformationProviderGenericTests.cs b/src/Migrator.Tests/Providers/SQLServer/SqlServerTransformationProviderGenericTests.cs index 6b483edc..f89967d6 100644 --- a/src/Migrator.Tests/Providers/SQLServer/SqlServerTransformationProviderGenericTests.cs +++ b/src/Migrator.Tests/Providers/SQLServer/SqlServerTransformationProviderGenericTests.cs @@ -1,16 +1,8 @@ -using System; using System.Data; -using System.Threading; using System.Threading.Tasks; using DotNetProjects.Migrator.Providers; using DotNetProjects.Migrator.Providers.Impl.SqlServer; -using DryIoc; -using Migrator.Tests.Database; -using Migrator.Tests.Database.Interfaces; using Migrator.Tests.Providers.Generic; -using Migrator.Tests.Settings; -using Migrator.Tests.Settings.Config; -using Migrator.Tests.Settings.Models; using NUnit.Framework; namespace Migrator.Tests.Providers.SQLServer; @@ -22,7 +14,7 @@ public class SqlServerTransformationProviderGenericTests : TransformationProvide [SetUp] public async Task SetUpAsync() { - dfdfg + await BeginSQLServerTransactionAsync(); AddDefaultTable(); } diff --git a/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_GetColumnsTests.cs b/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_GetColumnsTests.cs index da14ce25..bf4b41db 100644 --- a/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_GetColumnsTests.cs +++ b/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_GetColumnsTests.cs @@ -1,15 +1,22 @@ using System.Linq; +using System.Threading.Tasks; using DotNetProjects.Migrator.Framework; using DotNetProjects.Migrator.Providers.Impl.SQLite; -using Migrator.Tests.Providers.SQLite.Base; +using Migrator.Tests.Providers.Generic; using NUnit.Framework; namespace Migrator.Tests.Providers.SQLite; [TestFixture] [Category("SQLite")] -public class SQLiteTransformationProvider_GetColumnsTests : SQLiteTransformationProviderTestBase +public class SQLiteTransformationProvider_GetColumnsTests : TransformationProvider_GetColumns_GenericTests { + [SetUp] + public async Task SetUpAsync() + { + await BeginSQLiteTransactionAsync(); + } + [Test] public void GetColumns_UniqueButNotPrimaryKey_ReturnsFalse() { From 2df78f48d16401d74cddd5fe55d57b1a94b86aad Mon Sep 17 00:00:00 2001 From: JaBistDuNarrisch Date: Wed, 13 Aug 2025 10:04:21 +0200 Subject: [PATCH 05/27] Renamings --- ...r_asdf.cs => OracleTransformationProvider_GetColumns_Tests.cs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/Migrator.Tests/Providers/OracleProvider/{OracleTransformationProvider_asdf.cs => OracleTransformationProvider_GetColumns_Tests.cs} (100%) diff --git a/src/Migrator.Tests/Providers/OracleProvider/OracleTransformationProvider_asdf.cs b/src/Migrator.Tests/Providers/OracleProvider/OracleTransformationProvider_GetColumns_Tests.cs similarity index 100% rename from src/Migrator.Tests/Providers/OracleProvider/OracleTransformationProvider_asdf.cs rename to src/Migrator.Tests/Providers/OracleProvider/OracleTransformationProvider_GetColumns_Tests.cs From ce77eae4fe5c0f4eb8259d6fade3a8eea54f4718 Mon Sep 17 00:00:00 2001 From: JaBistDuNarrisch Date: Wed, 13 Aug 2025 10:16:48 +0200 Subject: [PATCH 06/27] Minor changes --- .../Generic/TransformationProviderGenericMiscConstraintBase.cs | 2 +- .../Generic/TransformationProvider_GetColumns_GenericTests.cs | 2 ++ .../SQLite/SQLiteTransformationProvider_GetColumnsTests.cs | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Migrator.Tests/Providers/Generic/TransformationProviderGenericMiscConstraintBase.cs b/src/Migrator.Tests/Providers/Generic/TransformationProviderGenericMiscConstraintBase.cs index 17ea3d12..f93b2154 100644 --- a/src/Migrator.Tests/Providers/Generic/TransformationProviderGenericMiscConstraintBase.cs +++ b/src/Migrator.Tests/Providers/Generic/TransformationProviderGenericMiscConstraintBase.cs @@ -7,7 +7,7 @@ namespace Migrator.Tests.Providers.Generic; /// -/// Base class for Provider tests for all tests including constraint oriented tests. +/// Base class for provider tests for all tests including constraint oriented tests. /// public abstract class TransformationProviderGenericMiscConstraintBase : TransformationProviderGenericMiscTests { diff --git a/src/Migrator.Tests/Providers/Generic/TransformationProvider_GetColumns_GenericTests.cs b/src/Migrator.Tests/Providers/Generic/TransformationProvider_GetColumns_GenericTests.cs index 8fb54927..07f2a092 100644 --- a/src/Migrator.Tests/Providers/Generic/TransformationProvider_GetColumns_GenericTests.cs +++ b/src/Migrator.Tests/Providers/Generic/TransformationProvider_GetColumns_GenericTests.cs @@ -12,6 +12,8 @@ namespace Migrator.Tests.Providers.Generic; /// public abstract class TransformationProvider_GetColumns_GenericTests : TransformationProviderBase { + + [Test] public void GetColumns_DefaultValues_Succeeds() { diff --git a/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_GetColumnsTests.cs b/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_GetColumnsTests.cs index bf4b41db..7d25dd2d 100644 --- a/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_GetColumnsTests.cs +++ b/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_GetColumnsTests.cs @@ -101,7 +101,7 @@ public void GetColumns_AddUniqueWithTwoColumns_NoUniqueOnColumnLevel() Assert.That(columns[0].ColumnProperty, Is.EqualTo(ColumnProperty.Null)); } - [Test, Description("Add index. Should be added and detected as index")] + [Test, Description("Add index. The index should be added and then being detected as index.")] public void GetSQLiteTableInfo_GetIndexesAndColumnsWithIndex_NoUniqueOnTheColumnsAndIndexExists() { // Arrange From a584a2cef33c0797b31a2f78d02a4f72e664b50b Mon Sep 17 00:00:00 2001 From: JaBistDuNarrisch Date: Wed, 13 Aug 2025 14:02:24 +0200 Subject: [PATCH 07/27] Read data type in GetColumns in SQL Server --- ...rmationProvider_GetColumns_GenericTests.cs | 2 - .../PostgreSQLTransformationProvider.cs | 16 +-- .../SqlServerTransformationProvider.cs | 118 ++++++++++++++---- 3 files changed, 105 insertions(+), 31 deletions(-) diff --git a/src/Migrator.Tests/Providers/Generic/TransformationProvider_GetColumns_GenericTests.cs b/src/Migrator.Tests/Providers/Generic/TransformationProvider_GetColumns_GenericTests.cs index 07f2a092..8fb54927 100644 --- a/src/Migrator.Tests/Providers/Generic/TransformationProvider_GetColumns_GenericTests.cs +++ b/src/Migrator.Tests/Providers/Generic/TransformationProvider_GetColumns_GenericTests.cs @@ -12,8 +12,6 @@ namespace Migrator.Tests.Providers.Generic; /// public abstract class TransformationProvider_GetColumns_GenericTests : TransformationProviderBase { - - [Test] public void GetColumns_DefaultValues_Succeeds() { diff --git a/src/Migrator/Providers/Impl/PostgreSQL/PostgreSQLTransformationProvider.cs b/src/Migrator/Providers/Impl/PostgreSQL/PostgreSQLTransformationProvider.cs index 2c7da7fa..6f91d630 100644 --- a/src/Migrator/Providers/Impl/PostgreSQL/PostgreSQLTransformationProvider.cs +++ b/src/Migrator/Providers/Impl/PostgreSQL/PostgreSQLTransformationProvider.cs @@ -470,7 +470,7 @@ public override Column[] GetColumns(string table) { // We assume that the value was added using this migrator so we do not interpret things like '2 days 01:02:03' if you // added such format you will run into this exception. - throw new NotImplementedException($"Cannot interpret {defaultValueString} in column '{column.Name}' unexpected pattern."); + throw new NotImplementedException($"Cannot parse {defaultValueString} in column '{column.Name}' unexpected pattern."); } } else if (column.MigratorDbType == MigratorDbType.Boolean) @@ -488,7 +488,7 @@ public override Column[] GetColumns(string table) } else { - throw new NotImplementedException($"Cannot interpret {defaultValueString} in column '{column.Name}'"); + throw new NotImplementedException($"Cannot parse {defaultValueString} in column '{column.Name}'"); } } else if (column.MigratorDbType == MigratorDbType.DateTime || column.MigratorDbType == MigratorDbType.DateTime2) @@ -499,7 +499,7 @@ public override Column[] GetColumns(string table) if (!match.Success) { - throw new NotImplementedException($"Cannot interpret {defaultValueString} in column '{column.Name}'"); + throw new NotImplementedException($"Cannot parse {defaultValueString} in column '{column.Name}'"); } var timeString = match.Value; @@ -511,7 +511,7 @@ public override Column[] GetColumns(string table) } else { - throw new NotImplementedException($"Cannot interpret {defaultValueString} in column '{column.Name}'"); + throw new NotImplementedException($"Cannot parse {defaultValueString} in column '{column.Name}'"); } } else if (column.MigratorDbType == MigratorDbType.Guid) @@ -522,14 +522,14 @@ public override Column[] GetColumns(string table) if (!match.Success) { - throw new NotImplementedException($"Cannot interpret {defaultValueString} in column '{column.Name}'"); + throw new NotImplementedException($"Cannot parse {defaultValueString} in column '{column.Name}'"); } column.DefaultValue = Guid.Parse(match.Value); } else { - throw new NotImplementedException($"Cannot interpret {defaultValueString} in column '{column.Name}'"); + throw new NotImplementedException($"Cannot parse {defaultValueString} in column '{column.Name}'"); } } else if (column.MigratorDbType == MigratorDbType.Decimal) @@ -562,7 +562,7 @@ public override Column[] GetColumns(string table) if (!match.Success) { - throw new NotImplementedException($"Cannot interpret {defaultValueString} in column '{column.Name}'"); + throw new NotImplementedException($"Cannot pars {defaultValueString} in column '{column.Name}'"); } var singleQuoteString = match.Value; @@ -582,7 +582,7 @@ public override Column[] GetColumns(string table) } else { - throw new NotImplementedException($"Cannot interpret {defaultValueString} in column '{column.Name}'"); + throw new NotImplementedException($"Cannot parse {defaultValueString} in column '{column.Name}'"); } } else diff --git a/src/Migrator/Providers/Impl/SqlServer/SqlServerTransformationProvider.cs b/src/Migrator/Providers/Impl/SqlServer/SqlServerTransformationProvider.cs index 0a880890..eaa1ab59 100644 --- a/src/Migrator/Providers/Impl/SqlServer/SqlServerTransformationProvider.cs +++ b/src/Migrator/Providers/Impl/SqlServer/SqlServerTransformationProvider.cs @@ -16,6 +16,8 @@ using System.Collections.Generic; using System.Data; using System.Globalization; +using System.Linq; +using System.Text.RegularExpressions; using Index = DotNetProjects.Migrator.Framework.Index; namespace DotNetProjects.Migrator.Providers.Impl.SqlServer; @@ -339,12 +341,19 @@ public override Column[] GetColumns(string table) using ( var reader = ExecuteQuery(cmd, - string.Format("select COLUMN_NAME, IS_NULLABLE, DATA_TYPE, ISNULL(CHARACTER_MAXIMUM_LENGTH, NUMERIC_PRECISION), COLUMN_DEFAULT, NUMERIC_SCALE from INFORMATION_SCHEMA.COLUMNS where table_name = '{0}'", table))) + string.Format("SELECT COLUMN_NAME, IS_NULLABLE, DATA_TYPE, ISNULL(CHARACTER_MAXIMUM_LENGTH , NUMERIC_PRECISION), COLUMN_DEFAULT, NUMERIC_SCALE, CHARACTER_MAXIMUM_LENGTH from INFORMATION_SCHEMA.COLUMNS where table_name = '{0}'", table))) { while (reader.Read()) { var column = new Column(reader.GetString(0), DbType.String); + var defaultValueOrdinal = reader.GetOrdinal("COLUMN_DEFAULT"); + var dataTypeOrdinal = reader.GetOrdinal("DATA_TYPE"); + var characterMaximumLengthOrdinal = reader.GetOrdinal("CHARACTER_MAXIMUM_LENGTH"); + + var defaultValueString = reader.IsDBNull(defaultValueOrdinal) ? null : reader.GetString(defaultValueOrdinal).Trim(); + var characterMaximumLength = reader.IsDBNull(characterMaximumLengthOrdinal) ? (int?)null : reader.GetInt32(characterMaximumLengthOrdinal); + if (pkColumns.Contains(column.Name)) { column.ColumnProperty |= ColumnProperty.PrimaryKey; @@ -358,10 +367,69 @@ public override Column[] GetColumns(string table) var nullableStr = reader.GetString(1); var isNullable = nullableStr == "YES"; - if (!reader.IsDBNull(2)) + var dataTypeString = reader.GetString(dataTypeOrdinal); + + if (dataTypeString == "date") + { + column.MigratorDbType = MigratorDbType.Date; + } + else if (dataTypeString == "int") + { + column.MigratorDbType = MigratorDbType.Int32; + } + else if (dataTypeString == "smallint") + { + column.MigratorDbType = MigratorDbType.Int16; + } + else if (dataTypeString == "tinyint") + { + column.MigratorDbType = MigratorDbType.Byte; + } + else if (dataTypeString == "bit") + { + column.MigratorDbType = MigratorDbType.Boolean; + } + else if (dataTypeString == "money") + { + column.MigratorDbType = MigratorDbType.Currency; + } + else if (dataTypeString == "float") + { + column.MigratorDbType = MigratorDbType.Double; + } + else if (new[] { "text", "nchar", "ntext", "varchar", "nvarchar" }.Contains(dataTypeString)) + { + // We use string for all string-like data types. + column.MigratorDbType = MigratorDbType.String; + column.Size = characterMaximumLength.Value; + } + else if (dataTypeString == "decimal") + { + column.MigratorDbType = MigratorDbType.Decimal; + } + else if (dataTypeString == "datetime") { - var type = reader.GetString(2); - column.Type = Dialect.GetDbTypeFromString(type); + column.MigratorDbType = MigratorDbType.DateTime; + } + else if (dataTypeString == "datetime2") + { + column.MigratorDbType = MigratorDbType.DateTime2; + } + else if (dataTypeString == "datetimeoffset") + { + column.MigratorDbType = MigratorDbType.DateTimeOffset; + } + else if (dataTypeString == "binary" || dataTypeString == "varbinary") + { + column.MigratorDbType = MigratorDbType.Binary; + } + else if (dataTypeString == "uniqueidentifier") + { + column.MigratorDbType = MigratorDbType.Guid; + } + else + { + throw new NotImplementedException($"The data type '{dataTypeString}' is not implemented yet. Please file an issue."); } if (!reader.IsDBNull(3)) @@ -369,19 +437,8 @@ public override Column[] GetColumns(string table) column.Size = reader.GetInt32(3); } - if (!reader.IsDBNull(4)) + if (defaultValueString != null) { - column.DefaultValue = reader.GetValue(4); - - if (column.DefaultValue.ToString()[1] == '(' || column.DefaultValue.ToString()[1] == '\'') - { - column.DefaultValue = column.DefaultValue.ToString().Substring(2, column.DefaultValue.ToString().Length - 4); // Example "((10))" or "('false')" - } - else - { - column.DefaultValue = column.DefaultValue.ToString().Substring(1, column.DefaultValue.ToString().Length - 2); // Example "(CONVERT([datetime],'20000101',(112)))" - } - if (column.Type == DbType.Int16 || column.Type == DbType.Int32 || column.Type == DbType.Int64) { column.DefaultValue = long.Parse(column.DefaultValue.ToString()); @@ -400,20 +457,27 @@ public override Column[] GetColumns(string table) } else if (column.Type == DbType.DateTime || column.Type == DbType.DateTime2) { - if (column.DefaultValue is string defValCv && defValCv.StartsWith("CONVERT(")) + // (CONVERT([datetime],'2000-01-02 03:04:05.000',(121))) + // 121 is a pattern: it contains milliseconds + // Search for 121 here: https://learn.microsoft.com/de-de/sql/t-sql/functions/cast-and-convert-transact-sql?view=sql-server-ver17 + var regexDateTimeConvert121 = new Regex(@"(?<=^\(CONVERT\([\[]+datetime[\]]+,')[^']+(?='\s*,\s*\(121\s*\)\)\)$)"); + var match121 = regexDateTimeConvert121.Match(defaultValueString); + + if (match121.Success) { - var dt = defValCv.Substring((defValCv.IndexOf("'") + 1), defValCv.IndexOf("'", defValCv.IndexOf("'") + 1) - defValCv.IndexOf("'") - 1); - var d = DateTime.ParseExact(dt, "yyyy-MM-dd HH:mm:ss.fff", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal); - column.DefaultValue = d; + // We convert to UTC since we restrict date time default values to UTC on default value definition. + column.DefaultValue = DateTime.ParseExact(match121.Value, "yyyy-MM-dd HH:mm:ss.fff", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal); } - else if (column.DefaultValue is string defVal) + else if (defaultValueString is string defVal) { + // Not tested var dt = defVal; if (defVal.StartsWith("'")) { dt = defVal.Substring(1, defVal.Length - 2); } + // We convert to UTC since we restrict date time default values to UTC on default value definition. var d = DateTime.ParseExact(dt, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal); column.DefaultValue = d; } @@ -427,6 +491,7 @@ public override Column[] GetColumns(string table) if (column.DefaultValue is string defVal) { var dt = defVal; + if (defVal.StartsWith("'")) { dt = defVal.Substring(1, defVal.Length - 2); @@ -436,6 +501,17 @@ public override Column[] GetColumns(string table) column.DefaultValue = d; } } + else if (column.MigratorDbType == MigratorDbType.Decimal) + { + // We assume ((1.234)) + var decimalString = defaultValueString.Replace("(", "").Replace(")", ""); + + column.DefaultValue = decimal.Parse(decimalString, CultureInfo.InvariantCulture); + } + else + { + throw new NotImplementedException($"Cannot parse the default value of {column.Name}. Type '' is not implemented yet."); + } } if (!reader.IsDBNull(5)) { From f67655242836f3bce7a0e63d1ba9dd0d82a29788 Mon Sep 17 00:00:00 2001 From: JaBistDuNarrisch Date: Wed, 13 Aug 2025 14:45:08 +0200 Subject: [PATCH 08/27] Extended parser for SQL Server default values. Test is now green for SQL Server --- ...rmationProvider_GetColumns_GenericTests.cs | 4 +- .../PostgreSQLTransformationProvider.cs | 2 +- .../SqlServerTransformationProvider.cs | 74 +++++++++++++------ 3 files changed, 56 insertions(+), 24 deletions(-) diff --git a/src/Migrator.Tests/Providers/Generic/TransformationProvider_GetColumns_GenericTests.cs b/src/Migrator.Tests/Providers/Generic/TransformationProvider_GetColumns_GenericTests.cs index 8fb54927..7fc51af5 100644 --- a/src/Migrator.Tests/Providers/Generic/TransformationProvider_GetColumns_GenericTests.cs +++ b/src/Migrator.Tests/Providers/Generic/TransformationProvider_GetColumns_GenericTests.cs @@ -49,7 +49,7 @@ public void GetColumns_DefaultValues_Succeeds() new Column(int64ColumnName2, DbType.Int64, defaultValue: 0), new Column(stringColumnName1, DbType.String, defaultValue: "Hello"), new Column(binaryColumnName1, DbType.Binary, defaultValue: new byte[] { 12, 32, 34 }), - new Column(doubleColumnName1, DbType.Double, defaultValue: 84.874596565) + new Column(doubleColumnName1, DbType.Double, defaultValue: 84.874596567) ); // Act @@ -77,6 +77,6 @@ public void GetColumns_DefaultValues_Succeeds() Assert.That(int64Column1.DefaultValue, Is.EqualTo(88)); Assert.That(stringColumn1.DefaultValue, Is.EqualTo("Hello")); Assert.That(binarycolumn1.DefaultValue, Is.EqualTo(new byte[] { 12, 32, 34 })); - Assert.That(doubleColumn1.DefaultValue, Is.EqualTo(84.874596565)); + Assert.That(doubleColumn1.DefaultValue, Is.EqualTo(84.874596567)); } } diff --git a/src/Migrator/Providers/Impl/PostgreSQL/PostgreSQLTransformationProvider.cs b/src/Migrator/Providers/Impl/PostgreSQL/PostgreSQLTransformationProvider.cs index 6f91d630..34f04aee 100644 --- a/src/Migrator/Providers/Impl/PostgreSQL/PostgreSQLTransformationProvider.cs +++ b/src/Migrator/Providers/Impl/PostgreSQL/PostgreSQLTransformationProvider.cs @@ -562,7 +562,7 @@ public override Column[] GetColumns(string table) if (!match.Success) { - throw new NotImplementedException($"Cannot pars {defaultValueString} in column '{column.Name}'"); + throw new NotImplementedException($"Cannot parse {defaultValueString} in column '{column.Name}'"); } var singleQuoteString = match.Value; diff --git a/src/Migrator/Providers/Impl/SqlServer/SqlServerTransformationProvider.cs b/src/Migrator/Providers/Impl/SqlServer/SqlServerTransformationProvider.cs index eaa1ab59..35c443ee 100644 --- a/src/Migrator/Providers/Impl/SqlServer/SqlServerTransformationProvider.cs +++ b/src/Migrator/Providers/Impl/SqlServer/SqlServerTransformationProvider.cs @@ -377,6 +377,10 @@ public override Column[] GetColumns(string table) { column.MigratorDbType = MigratorDbType.Int32; } + else if (dataTypeString == "bigint") + { + column.MigratorDbType = MigratorDbType.Int64; + } else if (dataTypeString == "smallint") { column.MigratorDbType = MigratorDbType.Int16; @@ -439,21 +443,42 @@ public override Column[] GetColumns(string table) if (defaultValueString != null) { + var bracesStrippedString = defaultValueString.Replace("(", "").Replace(")", "").Trim(); + var bracesAndSingleQuoteStrippedString = bracesStrippedString.Replace("'", ""); + if (column.Type == DbType.Int16 || column.Type == DbType.Int32 || column.Type == DbType.Int64) { - column.DefaultValue = long.Parse(column.DefaultValue.ToString()); + column.DefaultValue = long.Parse(bracesStrippedString, CultureInfo.InvariantCulture); } else if (column.Type == DbType.UInt16 || column.Type == DbType.UInt32 || column.Type == DbType.UInt64) { - column.DefaultValue = ulong.Parse(column.DefaultValue.ToString()); + column.DefaultValue = ulong.Parse(bracesStrippedString, CultureInfo.InvariantCulture); } else if (column.Type == DbType.Double || column.Type == DbType.Single) { - column.DefaultValue = double.Parse(column.DefaultValue.ToString()); + column.DefaultValue = double.Parse(bracesAndSingleQuoteStrippedString, CultureInfo.InvariantCulture); } else if (column.Type == DbType.Boolean) { - column.DefaultValue = column.DefaultValue.ToString().Trim() == "1" || column.DefaultValue.ToString().Trim().ToUpper() == "TRUE" || column.DefaultValue.ToString().Trim() == "YES"; + var truthy = new string[] { "'TRUE'", "1" }; + var falsy = new string[] { "'FALSE'", "0" }; + + if (truthy.Contains(bracesStrippedString)) + { + column.DefaultValue = true; + } + else if (falsy.Contains(bracesStrippedString)) + { + column.DefaultValue = false; + } + else if (bracesStrippedString == "NULL") + { + column.DefaultValue = null; + } + else + { + throw new NotImplementedException($"Cannot parse the boolean default value '{defaultValueString}' of column '{column.Name}'"); + } } else if (column.Type == DbType.DateTime || column.Type == DbType.DateTime2) { @@ -478,8 +503,7 @@ public override Column[] GetColumns(string table) } // We convert to UTC since we restrict date time default values to UTC on default value definition. - var d = DateTime.ParseExact(dt, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal); - column.DefaultValue = d; + column.DefaultValue = DateTime.ParseExact(dt, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal); } else { @@ -488,29 +512,37 @@ public override Column[] GetColumns(string table) } else if (column.Type == DbType.Guid) { - if (column.DefaultValue is string defVal) - { - var dt = defVal; - - if (defVal.StartsWith("'")) - { - dt = defVal.Substring(1, defVal.Length - 2); - } - - var d = Guid.Parse(dt); - column.DefaultValue = d; - } + column.DefaultValue = Guid.Parse(bracesAndSingleQuoteStrippedString); } else if (column.MigratorDbType == MigratorDbType.Decimal) { // We assume ((1.234)) - var decimalString = defaultValueString.Replace("(", "").Replace(")", ""); + column.DefaultValue = decimal.Parse(bracesStrippedString, CultureInfo.InvariantCulture); + } + else if (column.MigratorDbType == MigratorDbType.String) + { + column.DefaultValue = bracesAndSingleQuoteStrippedString; + } + else if (column.MigratorDbType == MigratorDbType.Binary) + { + if (bracesStrippedString.StartsWith("0x")) + { + var hexString = bracesStrippedString.Substring(2); + + // Not available in old .NET version: Convert.FromHexString(hexString); - column.DefaultValue = decimal.Parse(decimalString, CultureInfo.InvariantCulture); + column.DefaultValue = Enumerable.Range(0, hexString.Length / 2) + .Select(x => Convert.ToByte(hexString.Substring(x * 2, 2), 16)) + .ToArray(); + } + else + { + throw new NotImplementedException($"Cannot parse the binary default value of '{column.Name}'. The value is '{defaultValueString}'"); + } } else { - throw new NotImplementedException($"Cannot parse the default value of {column.Name}. Type '' is not implemented yet."); + throw new NotImplementedException($"Cannot parse the default value of {column.Name} type '{column.MigratorDbType}'. It is not yet implemented - file an issue."); } } if (!reader.IsDBNull(5)) From 7048961fcb570b97c3f5a0a4cef677767d6c5b7e Mon Sep 17 00:00:00 2001 From: JaBistDuNarrisch Date: Wed, 13 Aug 2025 17:57:23 +0200 Subject: [PATCH 09/27] Added changes to GetColumns in Oracle. --- ...rmationProvider_GetColumns_GenericTests.cs | 22 +- .../Providers/Impl/Oracle/OracleDialect.cs | 22 +- .../Oracle/OracleTransformationProvider.cs | 201 +++++++++--------- 3 files changed, 134 insertions(+), 111 deletions(-) diff --git a/src/Migrator.Tests/Providers/Generic/TransformationProvider_GetColumns_GenericTests.cs b/src/Migrator.Tests/Providers/Generic/TransformationProvider_GetColumns_GenericTests.cs index 7fc51af5..4a6222eb 100644 --- a/src/Migrator.Tests/Providers/Generic/TransformationProvider_GetColumns_GenericTests.cs +++ b/src/Migrator.Tests/Providers/Generic/TransformationProvider_GetColumns_GenericTests.cs @@ -56,17 +56,17 @@ public void GetColumns_DefaultValues_Succeeds() var columns = Provider.GetColumns(testTableName); // Assert - var dateTimeColumn1 = columns.Single(x => x.Name == dateTimeColumnName1); - var dateTimeColumn2 = columns.Single(x => x.Name == dateTimeColumnName2); - var decimalColumn1 = columns.Single(x => x.Name == decimalColumnName1); - var guidColumn1 = columns.Single(x => x.Name == guidColumnName1); - var booleanColumn1 = columns.Single(x => x.Name == booleanColumnName1); - var int32Column1 = columns.Single(x => x.Name == int32ColumnName1); - var int64Column1 = columns.Single(x => x.Name == int64ColumnName1); - var int64Column2 = columns.Single(x => x.Name == int64ColumnName2); - var stringColumn1 = columns.Single(x => x.Name == stringColumnName1); - var binarycolumn1 = columns.Single(x => x.Name == binaryColumnName1); - var doubleColumn1 = columns.Single(x => x.Name == doubleColumnName1); + var dateTimeColumn1 = columns.Single(x => x.Name.Equals(dateTimeColumnName1, StringComparison.OrdinalIgnoreCase)); + var dateTimeColumn2 = columns.Single(x => x.Name.Equals(dateTimeColumnName2, StringComparison.OrdinalIgnoreCase)); + var decimalColumn1 = columns.Single(x => x.Name.Equals(decimalColumnName1, StringComparison.OrdinalIgnoreCase)); + var guidColumn1 = columns.Single(x => x.Name.Equals(guidColumnName1, StringComparison.OrdinalIgnoreCase)); + var booleanColumn1 = columns.Single(x => x.Name.Equals(booleanColumnName1, StringComparison.OrdinalIgnoreCase)); + var int32Column1 = columns.Single(x => x.Name.Equals(int32ColumnName1, StringComparison.OrdinalIgnoreCase)); + var int64Column1 = columns.Single(x => x.Name.Equals(int64ColumnName1, StringComparison.OrdinalIgnoreCase)); + var int64Column2 = columns.Single(x => x.Name.Equals(int64ColumnName2, StringComparison.OrdinalIgnoreCase)); + var stringColumn1 = columns.Single(x => x.Name.Equals(stringColumnName1, StringComparison.OrdinalIgnoreCase)); + var binarycolumn1 = columns.Single(x => x.Name.Equals(binaryColumnName1, StringComparison.OrdinalIgnoreCase)); + var doubleColumn1 = columns.Single(x => x.Name.Equals(doubleColumnName1, StringComparison.OrdinalIgnoreCase)); Assert.That(dateTimeColumn1.DefaultValue, Is.EqualTo(dateTimeDefaultValue)); Assert.That(dateTimeColumn2.DefaultValue, Is.EqualTo(dateTimeDefaultValue)); diff --git a/src/Migrator/Providers/Impl/Oracle/OracleDialect.cs b/src/Migrator/Providers/Impl/Oracle/OracleDialect.cs index cb51faf3..90a722fb 100644 --- a/src/Migrator/Providers/Impl/Oracle/OracleDialect.cs +++ b/src/Migrator/Providers/Impl/Oracle/OracleDialect.cs @@ -118,17 +118,31 @@ public override ColumnPropertiesMapper GetColumnMapper(Column column) public override string Default(object defaultValue) { - if (defaultValue.GetType().Equals(typeof(bool))) + if (defaultValue == null) { - return string.Format("DEFAULT {0}", (bool)defaultValue ? "1" : "0"); + return string.Empty; + } + + if (defaultValue is bool booleanValue) + { + return string.Format("DEFAULT {0}", booleanValue ? "1" : "0"); } else if (defaultValue is Guid) { return string.Format("DEFAULT HEXTORAW('{0}')", defaultValue.ToString().Replace("-", "")); } - else if (defaultValue is DateTime) + else if (defaultValue is DateTime dateTime) + { + return string.Format("DEFAULT TO_TIMESTAMP('{0}', 'YYYY-MM-DD HH24:MI:SS.FF')", dateTime.ToString("yyyy-MM-dd HH:mm:ss.ff")); + } + else if (defaultValue is string stringValue) + { + return $"DEFAULT '{stringValue}'"; + } + else if (defaultValue is byte[] byteArray) { - return string.Format("DEFAULT TO_TIMESTAMP('{0}', 'YYYY-MM-DD HH24:MI:SS.FF')", ((DateTime)defaultValue).ToString("yyyy-MM-dd HH:mm:ss.ff")); + var convertedString = BitConverter.ToString(byteArray).Replace("-", "").ToLower(); + return $"DEFAULT HEXTORAW('{convertedString}')"; } return base.Default(defaultValue); diff --git a/src/Migrator/Providers/Impl/Oracle/OracleTransformationProvider.cs b/src/Migrator/Providers/Impl/Oracle/OracleTransformationProvider.cs index f8795cff..fbf0d8d7 100644 --- a/src/Migrator/Providers/Impl/Oracle/OracleTransformationProvider.cs +++ b/src/Migrator/Providers/Impl/Oracle/OracleTransformationProvider.cs @@ -384,112 +384,139 @@ public override string[] GetTables() public override Column[] GetColumns(string table) { + var stringBuilder = new StringBuilder(); + stringBuilder.AppendLine("SELECT"); + stringBuilder.AppendLine(" COLUMN_NAME,"); + stringBuilder.AppendLine(" NULLABLE,"); + stringBuilder.AppendLine(" DATA_DEFAULT,"); + stringBuilder.AppendLine(" DATA_TYPE,"); + stringBuilder.AppendLine(" DATA_PRECISION,"); + stringBuilder.AppendLine(" DATA_SCALE,"); + stringBuilder.AppendLine(" CHAR_COL_DECL_LENGTH"); + stringBuilder.AppendLine($"FROM USER_TAB_COLUMNS WHERE LOWER(TABLE_NAME) = LOWER('{table}')"); + var columns = new List(); using (var cmd = CreateCommand()) - using ( - var reader = - ExecuteQuery(cmd, - string.Format( - "select column_name, data_type, data_length, data_precision, data_scale, NULLABLE, data_default FROM USER_TAB_COLUMNS WHERE lower(table_name) = '{0}'", - table.ToLower()))) + using (var reader = ExecuteQuery(cmd, stringBuilder.ToString())) { while (reader.Read()) { - var colName = reader[0].ToString(); - var colType = DbType.String; - var dataType = reader[1].ToString().ToLower(); - var isNullable = ParseBoolean(reader.GetValue(5)); - var defaultValue = reader.GetValue(6); + var columnNameOrdinal = reader.GetOrdinal("COLUMN_NAME"); + var nullableOrdinal = reader.GetOrdinal("NULLABLE"); + var dataDefaultOrdinal = reader.GetOrdinal("DATA_DEFAULT"); + var dataTypeOrdinal = reader.GetOrdinal("DATA_TYPE"); + var dataPrecisionOrdinal = reader.GetOrdinal("DATA_PRECISION"); + var dataScaleOrdinal = reader.GetOrdinal("DATA_SCALE"); + var charColDeclLengthOrdinal = reader.GetOrdinal("CHAR_COL_DECL_LENGTH"); + + var columnName = reader.GetString(columnNameOrdinal); + var isNullable = reader.GetString(nullableOrdinal) == "Y" ? true : false; + var dataDefaultString = reader.GetString(dataDefaultOrdinal); + var dataTypeString = reader.GetString(dataTypeOrdinal); + var dataPrecision = reader.IsDBNull(dataPrecisionOrdinal) ? (int?)null : reader.GetInt32(dataPrecisionOrdinal); + var dataScale = reader.IsDBNull(dataScaleOrdinal) ? (int?)null : reader.GetInt32(dataScaleOrdinal); + var charColDeclLength = reader.IsDBNull(charColDeclLengthOrdinal) ? (int?)null : reader.GetInt32(charColDeclLengthOrdinal); + + var column = new Column(columnName, DbType.String) + { + ColumnProperty = isNullable ? ColumnProperty.Null : ColumnProperty.NotNull + }; - if (dataType.Equals("number")) + if (dataTypeString == "number") { - var precision = Convert.ToInt32(reader.GetValue(3)); - var scale = Convert.ToInt32(reader.GetValue(4)); - if (scale == 0) + var precision = dataPrecision; + var scale = dataScale; + + if (scale > 0) { - colType = precision <= 10 ? DbType.Int16 : DbType.Int64; + column.MigratorDbType = MigratorDbType.Decimal; } else { - colType = DbType.Decimal; + if (0 <= precision && precision <= 4) + { + column.MigratorDbType = MigratorDbType.Int16; + } + else if (5 <= precision && precision <= 10) + { + column.MigratorDbType = MigratorDbType.Int32; + } + else if (10 <= precision && precision <= 18) + { + column.MigratorDbType = MigratorDbType.Int64; + } + else + { + throw new NotSupportedException("No support for greater numbers than 18 digits"); + } } } - else if (dataType.StartsWith("timestamp") || dataType.Equals("date")) + else if (dataTypeString.StartsWith("TIMESTAMP") || dataTypeString.Equals("date")) { - colType = DbType.DateTime; + column.MigratorDbType = MigratorDbType.DateTime; } - var columnProperties = (isNullable) ? ColumnProperty.Null : ColumnProperty.NotNull; - var column = new Column(colName, colType, columnProperties); - - if (defaultValue != null && defaultValue != DBNull.Value) - { - column.DefaultValue = defaultValue; - } - - if (column.DefaultValue is string && ((string)column.DefaultValue).StartsWith("'") && ((string)column.DefaultValue).EndsWith("'")) - { - column.DefaultValue = ((string)column.DefaultValue).Substring(1, ((string)column.DefaultValue).Length - 2); - } - - if ((column.DefaultValue is string s && !string.IsNullOrEmpty(s)) || - column.DefaultValue is not string && column.DefaultValue != null) + if (dataDefaultString != null) { - if (column.Type == DbType.Int16 || column.Type == DbType.Int32 || column.Type == DbType.Int64) + if ((column.DefaultValue is string s && !string.IsNullOrEmpty(s)) || + column.DefaultValue is not string && column.DefaultValue != null) { - column.DefaultValue = long.Parse(column.DefaultValue.ToString()); - } - else if (column.Type == DbType.UInt16 || column.Type == DbType.UInt32 || column.Type == DbType.UInt64) - { - column.DefaultValue = ulong.Parse(column.DefaultValue.ToString()); - } - else if (column.Type == DbType.Double || column.Type == DbType.Single) - { - column.DefaultValue = double.Parse(column.DefaultValue.ToString()); - } - else if (column.Type == DbType.Boolean) - { - column.DefaultValue = column.DefaultValue.ToString().Trim() == "1" || column.DefaultValue.ToString().Trim().ToUpper() == "TRUE"; - } - else if (column.Type == DbType.DateTime || column.Type == DbType.DateTime2) - { - if (column.DefaultValue is string defValCv && defValCv.StartsWith("TO_TIMESTAMP(")) + if (column.Type == DbType.Int16 || column.Type == DbType.Int32 || column.Type == DbType.Int64) { - var dt = defValCv.Substring((defValCv.IndexOf("'") + 1), defValCv.IndexOf("'", defValCv.IndexOf("'") + 1) - defValCv.IndexOf("'") - 1); - var d = DateTime.ParseExact(dt, "yyyy-MM-dd HH:mm:ss.ff", CultureInfo.InvariantCulture); - column.DefaultValue = d; + column.DefaultValue = long.Parse(column.DefaultValue.ToString()); } - else if (column.DefaultValue is string defVal) + else if (column.Type == DbType.UInt16 || column.Type == DbType.UInt32 || column.Type == DbType.UInt64) { - var dt = defVal; - if (defVal.StartsWith("'")) - { - dt = defVal.Substring(1, defVal.Length - 2); - } - - var d = DateTime.ParseExact(dt, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture); - column.DefaultValue = d; + column.DefaultValue = ulong.Parse(column.DefaultValue.ToString()); } - } - else if (column.Type == DbType.Guid) - { - if (column.DefaultValue is string defValCv && defValCv.StartsWith("HEXTORAW(")) + else if (column.Type == DbType.Double || column.Type == DbType.Single) + { + column.DefaultValue = double.Parse(column.DefaultValue.ToString()); + } + else if (column.Type == DbType.Boolean) { - var dt = defValCv.Substring((defValCv.IndexOf("'") + 1), defValCv.IndexOf("'", defValCv.IndexOf("'") + 1) - defValCv.IndexOf("'") - 1); - var d = Guid.Parse(dt); - column.DefaultValue = d; + column.DefaultValue = column.DefaultValue.ToString().Trim() == "1" || column.DefaultValue.ToString().Trim().ToUpper() == "TRUE"; } - else if (column.DefaultValue is string defVal) + else if (column.Type == DbType.DateTime || column.Type == DbType.DateTime2) { - var dt = defVal; - if (defVal.StartsWith("'")) + if (column.DefaultValue is string defValCv && defValCv.StartsWith("TO_TIMESTAMP(")) { - dt = defVal.Substring(1, defVal.Length - 2); + var dt = defValCv.Substring((defValCv.IndexOf("'") + 1), defValCv.IndexOf("'", defValCv.IndexOf("'") + 1) - defValCv.IndexOf("'") - 1); + var d = DateTime.ParseExact(dt, "yyyy-MM-dd HH:mm:ss.ff", CultureInfo.InvariantCulture); + column.DefaultValue = d; + } + else if (column.DefaultValue is string defVal) + { + var dt = defVal; + if (defVal.StartsWith("'")) + { + dt = defVal.Substring(1, defVal.Length - 2); + } + + var d = DateTime.ParseExact(dt, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture); + column.DefaultValue = d; + } + } + else if (column.Type == DbType.Guid) + { + if (column.DefaultValue is string defValCv && defValCv.StartsWith("HEXTORAW(")) + { + var dt = defValCv.Substring((defValCv.IndexOf("'") + 1), defValCv.IndexOf("'", defValCv.IndexOf("'") + 1) - defValCv.IndexOf("'") - 1); + var d = Guid.Parse(dt); + column.DefaultValue = d; + } + else if (column.DefaultValue is string defVal) + { + var dt = defVal; + if (defVal.StartsWith("'")) + { + dt = defVal.Substring(1, defVal.Length - 2); + } + + var d = Guid.Parse(dt); + column.DefaultValue = d; } - - var d = Guid.Parse(dt); - column.DefaultValue = d; } } } @@ -501,24 +528,6 @@ public override Column[] GetColumns(string table) return columns.ToArray(); } - private bool ParseBoolean(object value) - { - if (value is string) - { - if ("N" == (string)value) - { - return false; - } - - if ("Y" == (string)value) - { - return true; - } - } - - return Convert.ToBoolean(value); - } - public override string GenerateParameterNameParameter(int index) { return "p" + index; From 2e922916f5846393557895e7dc1c3f4a1c0b95b1 Mon Sep 17 00:00:00 2001 From: JaBistDuNarrisch Date: Thu, 14 Aug 2025 15:09:45 +0200 Subject: [PATCH 10/27] Added changes to GetColumns in Oracle. --- ...rmationProvider_GetColumns_GenericTests.cs | 2 +- .../Impl/Oracle/Models/UserTabColumns.cs | 7 + .../Providers/Impl/Oracle/OracleDialect.cs | 5 +- .../Oracle/OracleTransformationProvider.cs | 239 +++++++++++++----- 4 files changed, 190 insertions(+), 63 deletions(-) create mode 100644 src/Migrator/Providers/Impl/Oracle/Models/UserTabColumns.cs diff --git a/src/Migrator.Tests/Providers/Generic/TransformationProvider_GetColumns_GenericTests.cs b/src/Migrator.Tests/Providers/Generic/TransformationProvider_GetColumns_GenericTests.cs index 4a6222eb..ee7e3760 100644 --- a/src/Migrator.Tests/Providers/Generic/TransformationProvider_GetColumns_GenericTests.cs +++ b/src/Migrator.Tests/Providers/Generic/TransformationProvider_GetColumns_GenericTests.cs @@ -49,7 +49,7 @@ public void GetColumns_DefaultValues_Succeeds() new Column(int64ColumnName2, DbType.Int64, defaultValue: 0), new Column(stringColumnName1, DbType.String, defaultValue: "Hello"), new Column(binaryColumnName1, DbType.Binary, defaultValue: new byte[] { 12, 32, 34 }), - new Column(doubleColumnName1, DbType.Double, defaultValue: 84.874596567) + new Column(doubleColumnName1, DbType.Double, defaultValue: 84.874596567) { Precision = 19, Scale = 10 } ); // Act diff --git a/src/Migrator/Providers/Impl/Oracle/Models/UserTabColumns.cs b/src/Migrator/Providers/Impl/Oracle/Models/UserTabColumns.cs new file mode 100644 index 00000000..413a3d79 --- /dev/null +++ b/src/Migrator/Providers/Impl/Oracle/Models/UserTabColumns.cs @@ -0,0 +1,7 @@ +namespace DotNetProjects.Migrator.Providers.Impl.Oracle.Models; + +public class UserTabColumns +{ + public string ColumnName { get; set; } + public string DataDefault { get; set; } +} \ No newline at end of file diff --git a/src/Migrator/Providers/Impl/Oracle/OracleDialect.cs b/src/Migrator/Providers/Impl/Oracle/OracleDialect.cs index 90a722fb..110309b7 100644 --- a/src/Migrator/Providers/Impl/Oracle/OracleDialect.cs +++ b/src/Migrator/Providers/Impl/Oracle/OracleDialect.cs @@ -1,5 +1,6 @@ using System; using System.Data; +using System.Linq; using DotNetProjects.Migrator.Framework; namespace DotNetProjects.Migrator.Providers.Impl.Oracle; @@ -37,6 +38,7 @@ public OracleDialect() RegisterColumnType(DbType.UInt32, "NUMBER(10,0)"); RegisterColumnType(DbType.UInt64, "NUMBER(20,0)"); RegisterColumnType(DbType.Single, "FLOAT(24)"); + RegisterColumnType(DbType.Double, "BINARY_DOUBLE"); RegisterColumnType(DbType.StringFixedLength, "NCHAR(255)"); RegisterColumnType(DbType.StringFixedLength, 2000, "NCHAR($l)"); RegisterColumnType(DbType.String, "NVARCHAR2(255)"); @@ -133,7 +135,8 @@ public override string Default(object defaultValue) } else if (defaultValue is DateTime dateTime) { - return string.Format("DEFAULT TO_TIMESTAMP('{0}', 'YYYY-MM-DD HH24:MI:SS.FF')", dateTime.ToString("yyyy-MM-dd HH:mm:ss.ff")); + var dateTimeString = dateTime.ToString("yyyy-MM-dd HH:mm:ss.ffff"); + return $"DEFAULT TO_TIMESTAMP('{dateTimeString}', 'YYYY-MM-DD HH24:MI:SS.FF4')"; } else if (defaultValue is string stringValue) { diff --git a/src/Migrator/Providers/Impl/Oracle/OracleTransformationProvider.cs b/src/Migrator/Providers/Impl/Oracle/OracleTransformationProvider.cs index fbf0d8d7..0a34e46c 100644 --- a/src/Migrator/Providers/Impl/Oracle/OracleTransformationProvider.cs +++ b/src/Migrator/Providers/Impl/Oracle/OracleTransformationProvider.cs @@ -1,11 +1,14 @@ using DotNetProjects.Migrator.Framework; +using DotNetProjects.Migrator.Providers.Impl.Oracle.Models; using DotNetProjects.Migrator.Providers.Models; using System; using System.Collections.Generic; using System.Data; using System.Globalization; using System.Linq; +using System.Linq.Expressions; using System.Text; +using System.Text.RegularExpressions; using ForeignKeyConstraint = DotNetProjects.Migrator.Framework.ForeignKeyConstraint; using Index = DotNetProjects.Migrator.Framework.Index; @@ -384,6 +387,9 @@ public override string[] GetTables() public override Column[] GetColumns(string table) { + var timestampRegex = new Regex(@"(?<=^TIMESTAMP\s+')[^']+(?=')", RegexOptions.IgnoreCase); + var timestampBaseFormat = "yyyy-MM-dd HH:mm:ss"; + var stringBuilder = new StringBuilder(); stringBuilder.AppendLine("SELECT"); stringBuilder.AppendLine(" COLUMN_NAME,"); @@ -395,6 +401,38 @@ public override Column[] GetColumns(string table) stringBuilder.AppendLine(" CHAR_COL_DECL_LENGTH"); stringBuilder.AppendLine($"FROM USER_TAB_COLUMNS WHERE LOWER(TABLE_NAME) = LOWER('{table}')"); + var stringBuilder2 = new StringBuilder(); + stringBuilder2.AppendLine("SELECT x.column_name, x.data_default"); + stringBuilder2.AppendLine("FROM XMLTABLE("); + stringBuilder2.AppendLine(" '/ROWSET/ROW'"); + stringBuilder2.AppendLine(" PASSING DBMS_XMLGEN.GETXMLTYPE("); + stringBuilder2.AppendLine($" 'SELECT column_name, data_default FROM user_tab_columns WHERE table_name = ''{table.ToUpperInvariant()}'''"); + stringBuilder2.AppendLine(" )"); + stringBuilder2.AppendLine(" COLUMNS"); + stringBuilder2.AppendLine(" column_name VARCHAR2(4000) PATH 'COLUMN_NAME',"); + stringBuilder2.AppendLine(" data_default VARCHAR2(4000) PATH 'DATA_DEFAULT'"); + stringBuilder2.AppendLine(") x"); + + List userTabColumns = []; + + using (var cmd = CreateCommand()) + using (var reader = ExecuteQuery(cmd, stringBuilder2.ToString())) + { + while (reader.Read()) + { + var columnNameOrdinal = reader.GetOrdinal("COLUMN_NAME"); + var dataDefaultOrdinal = reader.GetOrdinal("DATA_DEFAULT"); + + var userTabColumnsItem = new UserTabColumns + { + ColumnName = reader.IsDBNull(columnNameOrdinal) ? null : reader.GetString(columnNameOrdinal), + DataDefault = reader.IsDBNull(dataDefaultOrdinal) ? null : reader.GetString(dataDefaultOrdinal).Trim() + }; + + userTabColumns.Add(userTabColumnsItem); + } + } + var columns = new List(); using (var cmd = CreateCommand()) @@ -404,121 +442,200 @@ public override Column[] GetColumns(string table) { var columnNameOrdinal = reader.GetOrdinal("COLUMN_NAME"); var nullableOrdinal = reader.GetOrdinal("NULLABLE"); - var dataDefaultOrdinal = reader.GetOrdinal("DATA_DEFAULT"); var dataTypeOrdinal = reader.GetOrdinal("DATA_TYPE"); var dataPrecisionOrdinal = reader.GetOrdinal("DATA_PRECISION"); var dataScaleOrdinal = reader.GetOrdinal("DATA_SCALE"); var charColDeclLengthOrdinal = reader.GetOrdinal("CHAR_COL_DECL_LENGTH"); var columnName = reader.GetString(columnNameOrdinal); - var isNullable = reader.GetString(nullableOrdinal) == "Y" ? true : false; - var dataDefaultString = reader.GetString(dataDefaultOrdinal); - var dataTypeString = reader.GetString(dataTypeOrdinal); + var isNullable = reader.GetString(nullableOrdinal) == "Y"; + var dataTypeString = reader.GetString(dataTypeOrdinal).ToUpperInvariant(); var dataPrecision = reader.IsDBNull(dataPrecisionOrdinal) ? (int?)null : reader.GetInt32(dataPrecisionOrdinal); var dataScale = reader.IsDBNull(dataScaleOrdinal) ? (int?)null : reader.GetInt32(dataScaleOrdinal); var charColDeclLength = reader.IsDBNull(charColDeclLengthOrdinal) ? (int?)null : reader.GetInt32(charColDeclLengthOrdinal); + var dataDefaultString = userTabColumns.FirstOrDefault(x => x.ColumnName.Equals(columnName, StringComparison.OrdinalIgnoreCase))?.DataDefault; var column = new Column(columnName, DbType.String) { ColumnProperty = isNullable ? ColumnProperty.Null : ColumnProperty.NotNull }; - if (dataTypeString == "number") + // Oracle does not have unsigned types. All NUMBER types can hold positive or negative values so we do not return DbType.UIntX types. + if (dataTypeString.StartsWith("NUMBER") || dataTypeString.StartsWith("FLOAT")) { - var precision = dataPrecision; - var scale = dataScale; + column.Precision = dataPrecision; - if (scale > 0) + if (dataScale > 0) { + // Could also be Double column.MigratorDbType = MigratorDbType.Decimal; + column.Scale = dataScale; } else { - if (0 <= precision && precision <= 4) + if (dataPrecision.HasValue && 0 <= dataPrecision && dataPrecision <= 5) { column.MigratorDbType = MigratorDbType.Int16; } - else if (5 <= precision && precision <= 10) + else if (dataPrecision.HasValue && 6 <= dataPrecision && dataPrecision <= 10) { column.MigratorDbType = MigratorDbType.Int32; } - else if (10 <= precision && precision <= 18) + else if (dataPrecision == null || 11 <= dataPrecision) { + // Oracle allows up to 38 digits but in C# the maximum is Int64 and in Oracle there is no unsigned data type. column.MigratorDbType = MigratorDbType.Int64; } else { - throw new NotSupportedException("No support for greater numbers than 18 digits"); + throw new NotSupportedException(); } } } - else if (dataTypeString.StartsWith("TIMESTAMP") || dataTypeString.Equals("date")) + else if (dataTypeString.StartsWith("TIMESTAMP")) + { + var timestampNumberRegex = new Regex(@"(?<=^Timestamp\()[\d]+(?=\)$)", RegexOptions.IgnoreCase); + var timestampNumberMatch = timestampNumberRegex.Match(dataTypeString); + + if (timestampNumberMatch.Success) + { + // n in TIMESTAMP(n) is not retrievable using system tables so we need to extract it via regex. + column.Precision = int.Parse(timestampNumberMatch.Value); + column.MigratorDbType = column.Precision < 3 ? MigratorDbType.DateTime : MigratorDbType.DateTime2; + } + else + { + // 6 is the standard if we use TIMESTAMP without n like in TIMESTAMP(n) + column.Precision = 6; + column.MigratorDbType = MigratorDbType.DateTime2; + } + } + else if (dataTypeString == "DATE") + { + column.MigratorDbType = MigratorDbType.Date; + } + else if (dataTypeString == "RAW(16)") + { + // ambiguity - cannot distinguish between guid and binary + column.MigratorDbType = MigratorDbType.Guid; + } + else if (dataTypeString.StartsWith("RAW") || dataTypeString == "BLOB") + { + column.MigratorDbType = MigratorDbType.Binary; + } + else if (dataTypeString == "NVARCHAR2") + { + column.MigratorDbType = MigratorDbType.String; + } + else if (dataTypeString == "BINARY_FLOAT") + { + column.MigratorDbType = MigratorDbType.Single; + } + else if (dataTypeString == "BINARY_DOUBLE") + { + column.MigratorDbType = MigratorDbType.Double; + } + else { - column.MigratorDbType = MigratorDbType.DateTime; + throw new NotImplementedException(); } - if (dataDefaultString != null) + if (!string.IsNullOrWhiteSpace(dataDefaultString)) { - if ((column.DefaultValue is string s && !string.IsNullOrEmpty(s)) || - column.DefaultValue is not string && column.DefaultValue != null) + if (column.Type == DbType.Int16 || column.Type == DbType.Int32 || column.Type == DbType.Int64) { - if (column.Type == DbType.Int16 || column.Type == DbType.Int32 || column.Type == DbType.Int64) + column.DefaultValue = long.Parse(dataDefaultString, CultureInfo.InvariantCulture); + } + else if (column.Type == DbType.Double) + { + column.DefaultValue = double.Parse(dataDefaultString, CultureInfo.InvariantCulture); + } + else if (column.Type == DbType.Single) + { + column.DefaultValue = float.Parse(dataDefaultString, CultureInfo.InvariantCulture); + } + else if (column.Type == DbType.Decimal) + { + column.DefaultValue = decimal.Parse(dataDefaultString, CultureInfo.InvariantCulture); + } + else if (column.Type == DbType.Boolean) + { + column.DefaultValue = dataDefaultString == "1" || dataDefaultString.ToUpper() == "TRUE"; + } + else if (column.Type == DbType.DateTime || column.Type == DbType.DateTime2) + { + if (dataDefaultString.StartsWith("TO_TIMESTAMP(")) { - column.DefaultValue = long.Parse(column.DefaultValue.ToString()); + var expectedOracleToTimestampPattern = "YYYY-MM-DD HH24:MI:SS"; + + if (!dataDefaultString.Contains(expectedOracleToTimestampPattern)) + { + throw new NotSupportedException($"Not supported 'TO_TIMESTAMP' pattern. Expected pattern: {expectedOracleToTimestampPattern}"); + } + + var toTimestampRegex = new Regex(@"(?<=^TO_TIMESTAMP\(')[^']+(?=')", RegexOptions.IgnoreCase); + var toTimestampMatch = toTimestampRegex.Match(dataDefaultString); + var toTimestampDateTimeString = toTimestampMatch.Value; + + List formats = []; + + // add formats with .F, .FF, .FFF etc. + formats = Enumerable.Range(0, 20).Select((x, y) => $"{timestampBaseFormat}.{new string('F', y + 1)}").ToList(); + formats.Add(timestampBaseFormat); + + column.DefaultValue = DateTime.ParseExact(toTimestampDateTimeString, [.. formats], CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal); } - else if (column.Type == DbType.UInt16 || column.Type == DbType.UInt32 || column.Type == DbType.UInt64) + else if (timestampRegex.Match(dataDefaultString) is Match timestampMatch && timestampMatch.Success) { - column.DefaultValue = ulong.Parse(column.DefaultValue.ToString()); + var millisecondsPattern = column.Size == 0 ? string.Empty : $".{new string('F', column.Size)}"; + column.DefaultValue = DateTime.ParseExact(timestampMatch.Value, $"yyyy-MM-dd HH:mm:ss{millisecondsPattern}", CultureInfo.InvariantCulture); } - else if (column.Type == DbType.Double || column.Type == DbType.Single) + else { - column.DefaultValue = double.Parse(column.DefaultValue.ToString()); + // Could be system time in many variants + column.DefaultValue = dataDefaultString; } - else if (column.Type == DbType.Boolean) + } + else if (column.Type == DbType.Guid) + { + var hexToRawRegex = new Regex(@"(?<=^HEXTORAW\s*\(')[^']+(?=')", RegexOptions.IgnoreCase); + + if (hexToRawRegex.Match(dataDefaultString) is Match hexToRawMatch && hexToRawMatch.Success) { - column.DefaultValue = column.DefaultValue.ToString().Trim() == "1" || column.DefaultValue.ToString().Trim().ToUpper() == "TRUE"; + var bytes = Enumerable.Range(0, hexToRawMatch.Value.Length / 2) + .Select(x => Convert.ToByte(hexToRawMatch.Value.Substring(x * 2, 2), 16)) + .ToArray(); + + // Oracle uses Big-Endian + // Reverse first 4 bytes + var guidBytes = new byte[16]; + Array.Copy(bytes, 0, guidBytes, 0, 4); + Array.Reverse(guidBytes, 0, 4); + + // Reverse next 2 bytes + Array.Copy(bytes, 4, guidBytes, 4, 2); + Array.Reverse(guidBytes, 6, 2); + + // Copy remaining 8 bytes + Array.Copy(bytes, 8, guidBytes, 8, 8); + + column.DefaultValue = new Guid(guidBytes); } - else if (column.Type == DbType.DateTime || column.Type == DbType.DateTime2) + else if (dataDefaultString.StartsWith("'")) { - if (column.DefaultValue is string defValCv && defValCv.StartsWith("TO_TIMESTAMP(")) - { - var dt = defValCv.Substring((defValCv.IndexOf("'") + 1), defValCv.IndexOf("'", defValCv.IndexOf("'") + 1) - defValCv.IndexOf("'") - 1); - var d = DateTime.ParseExact(dt, "yyyy-MM-dd HH:mm:ss.ff", CultureInfo.InvariantCulture); - column.DefaultValue = d; - } - else if (column.DefaultValue is string defVal) - { - var dt = defVal; - if (defVal.StartsWith("'")) - { - dt = defVal.Substring(1, defVal.Length - 2); - } - - var d = DateTime.ParseExact(dt, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture); - column.DefaultValue = d; - } + var guidString = dataDefaultString.Substring(1, dataDefaultString.Length - 2); + + column.DefaultValue = Guid.Parse(guidString); } - else if (column.Type == DbType.Guid) + else { - if (column.DefaultValue is string defValCv && defValCv.StartsWith("HEXTORAW(")) - { - var dt = defValCv.Substring((defValCv.IndexOf("'") + 1), defValCv.IndexOf("'", defValCv.IndexOf("'") + 1) - defValCv.IndexOf("'") - 1); - var d = Guid.Parse(dt); - column.DefaultValue = d; - } - else if (column.DefaultValue is string defVal) - { - var dt = defVal; - if (defVal.StartsWith("'")) - { - dt = defVal.Substring(1, defVal.Length - 2); - } - - var d = Guid.Parse(dt); - column.DefaultValue = d; - } + column.DefaultValue = dataDefaultString; } } + else + { + column.DefaultValue = dataDefaultString; + } } columns.Add(column); From d7ac6ec6afceedf0c883291fb54dabe59b303001 Mon Sep 17 00:00:00 2001 From: JaBistDuNarrisch Date: Thu, 14 Aug 2025 16:14:51 +0200 Subject: [PATCH 11/27] Oracle test GetColumns_DefaultValues_Succeeds is green --- .../Providers/Impl/Oracle/OracleDialect.cs | 29 ++++++++- .../Oracle/OracleTransformationProvider.cs | 60 ++++++++++++++----- 2 files changed, 71 insertions(+), 18 deletions(-) diff --git a/src/Migrator/Providers/Impl/Oracle/OracleDialect.cs b/src/Migrator/Providers/Impl/Oracle/OracleDialect.cs index 110309b7..da6e958e 100644 --- a/src/Migrator/Providers/Impl/Oracle/OracleDialect.cs +++ b/src/Migrator/Providers/Impl/Oracle/OracleDialect.cs @@ -17,6 +17,8 @@ public OracleDialect() RegisterColumnType(DbType.Binary, "RAW(2000)"); RegisterColumnType(DbType.Binary, 2000, "RAW($l)"); RegisterColumnType(DbType.Binary, 2147483647, "BLOB"); + + // 23ai now has a native boolean data type but for backwards compatibility we keep using NUMBER(1,0) RegisterColumnType(DbType.Boolean, "NUMBER(1,0)"); RegisterColumnType(DbType.Byte, "NUMBER(3,0)"); RegisterColumnType(DbType.Currency, "NUMBER(19,1)"); @@ -129,9 +131,32 @@ public override string Default(object defaultValue) { return string.Format("DEFAULT {0}", booleanValue ? "1" : "0"); } - else if (defaultValue is Guid) + else if (defaultValue is Guid guid) { - return string.Format("DEFAULT HEXTORAW('{0}')", defaultValue.ToString().Replace("-", "")); + var bytes = guid.ToByteArray(); + + // Convert to big-endian format in Oracle + var oracleBytes = new byte[16]; + + // Reverse first 4 bytes + Array.Copy(bytes, 0, oracleBytes, 0, 4); + Array.Reverse(oracleBytes, 0, 4); + + // Reverse next 2 bytes + Array.Copy(bytes, 4, oracleBytes, 4, 2); + Array.Reverse(oracleBytes, 4, 2); + + // Reverse next 2 bytes + Array.Copy(bytes, 6, oracleBytes, 6, 2); + Array.Reverse(oracleBytes, 6, 2); + + // Copy remaining 8bytes + Array.Copy(bytes, 8, oracleBytes, 8, 8); + + // Convert to hex string + var hex = BitConverter.ToString(oracleBytes).Replace("-", ""); + + return $"DEFAULT HEXTORAW('{hex}')"; } else if (defaultValue is DateTime dateTime) { diff --git a/src/Migrator/Providers/Impl/Oracle/OracleTransformationProvider.cs b/src/Migrator/Providers/Impl/Oracle/OracleTransformationProvider.cs index 0a34e46c..728b5978 100644 --- a/src/Migrator/Providers/Impl/Oracle/OracleTransformationProvider.cs +++ b/src/Migrator/Providers/Impl/Oracle/OracleTransformationProvider.cs @@ -388,6 +388,7 @@ public override string[] GetTables() public override Column[] GetColumns(string table) { var timestampRegex = new Regex(@"(?<=^TIMESTAMP\s+')[^']+(?=')", RegexOptions.IgnoreCase); + var hexToRawRegex = new Regex(@"(?<=^HEXTORAW\s*\(')[^']+(?=')", RegexOptions.IgnoreCase); var timestampBaseFormat = "yyyy-MM-dd HH:mm:ss"; var stringBuilder = new StringBuilder(); @@ -396,6 +397,7 @@ public override Column[] GetColumns(string table) stringBuilder.AppendLine(" NULLABLE,"); stringBuilder.AppendLine(" DATA_DEFAULT,"); stringBuilder.AppendLine(" DATA_TYPE,"); + stringBuilder.AppendLine(" DATA_LENGTH,"); stringBuilder.AppendLine(" DATA_PRECISION,"); stringBuilder.AppendLine(" DATA_SCALE,"); stringBuilder.AppendLine(" CHAR_COL_DECL_LENGTH"); @@ -443,6 +445,7 @@ public override Column[] GetColumns(string table) var columnNameOrdinal = reader.GetOrdinal("COLUMN_NAME"); var nullableOrdinal = reader.GetOrdinal("NULLABLE"); var dataTypeOrdinal = reader.GetOrdinal("DATA_TYPE"); + var dataLengthOrdinal = reader.GetOrdinal("DATA_LENGTH"); var dataPrecisionOrdinal = reader.GetOrdinal("DATA_PRECISION"); var dataScaleOrdinal = reader.GetOrdinal("DATA_SCALE"); var charColDeclLengthOrdinal = reader.GetOrdinal("CHAR_COL_DECL_LENGTH"); @@ -450,6 +453,7 @@ public override Column[] GetColumns(string table) var columnName = reader.GetString(columnNameOrdinal); var isNullable = reader.GetString(nullableOrdinal) == "Y"; var dataTypeString = reader.GetString(dataTypeOrdinal).ToUpperInvariant(); + var dataLength = reader.IsDBNull(dataLengthOrdinal) ? (int?)null : reader.GetInt32(dataLengthOrdinal); var dataPrecision = reader.IsDBNull(dataPrecisionOrdinal) ? (int?)null : reader.GetInt32(dataPrecisionOrdinal); var dataScale = reader.IsDBNull(dataScaleOrdinal) ? (int?)null : reader.GetInt32(dataScaleOrdinal); var charColDeclLength = reader.IsDBNull(charColDeclLengthOrdinal) ? (int?)null : reader.GetInt32(charColDeclLengthOrdinal); @@ -473,7 +477,11 @@ public override Column[] GetColumns(string table) } else { - if (dataPrecision.HasValue && 0 <= dataPrecision && dataPrecision <= 5) + if (dataPrecision.HasValue && dataPrecision == 1) + { + column.MigratorDbType = MigratorDbType.Boolean; + } + else if (dataPrecision.HasValue && (dataPrecision == 0 || (2 <= dataPrecision && dataPrecision <= 5))) { column.MigratorDbType = MigratorDbType.Int16; } @@ -514,7 +522,7 @@ public override Column[] GetColumns(string table) { column.MigratorDbType = MigratorDbType.Date; } - else if (dataTypeString == "RAW(16)") + else if (dataTypeString == "RAW" && dataLength == 16) { // ambiguity - cannot distinguish between guid and binary column.MigratorDbType = MigratorDbType.Guid; @@ -535,6 +543,10 @@ public override Column[] GetColumns(string table) { column.MigratorDbType = MigratorDbType.Double; } + else if (dataTypeString == "BOOLEAN") + { + column.MigratorDbType = MigratorDbType.Boolean; + } else { throw new NotImplementedException(); @@ -598,8 +610,6 @@ public override Column[] GetColumns(string table) } else if (column.Type == DbType.Guid) { - var hexToRawRegex = new Regex(@"(?<=^HEXTORAW\s*\(')[^']+(?=')", RegexOptions.IgnoreCase); - if (hexToRawRegex.Match(dataDefaultString) is Match hexToRawMatch && hexToRawMatch.Success) { var bytes = Enumerable.Range(0, hexToRawMatch.Value.Length / 2) @@ -607,19 +617,11 @@ public override Column[] GetColumns(string table) .ToArray(); // Oracle uses Big-Endian - // Reverse first 4 bytes - var guidBytes = new byte[16]; - Array.Copy(bytes, 0, guidBytes, 0, 4); - Array.Reverse(guidBytes, 0, 4); - - // Reverse next 2 bytes - Array.Copy(bytes, 4, guidBytes, 4, 2); - Array.Reverse(guidBytes, 6, 2); - - // Copy remaining 8 bytes - Array.Copy(bytes, 8, guidBytes, 8, 8); + Array.Reverse(bytes, 0, 4); + Array.Reverse(bytes, 4, 2); + Array.Reverse(bytes, 6, 2); - column.DefaultValue = new Guid(guidBytes); + column.DefaultValue = new Guid(bytes); } else if (dataDefaultString.StartsWith("'")) { @@ -632,6 +634,32 @@ public override Column[] GetColumns(string table) column.DefaultValue = dataDefaultString; } } + else if (column.Type == DbType.String) + { + var contentRegex = new Regex(@"(?<=^').*(?='$)"); + + if (contentRegex.Match(dataDefaultString) is Match contentMatch && contentMatch.Success) + { + column.DefaultValue = contentMatch.Value; + } + else + { + throw new Exception($"Cannot parse string column '{column.Name}'"); + } + } + else if (column.Type == DbType.Binary) + { + if (hexToRawRegex.Match(dataDefaultString) is Match hexToRawMatch && hexToRawMatch.Success) + { + column.DefaultValue = Enumerable.Range(0, hexToRawMatch.Value.Length / 2) + .Select(x => Convert.ToByte(hexToRawMatch.Value.Substring(x * 2, 2), 16)) + .ToArray(); + } + else + { + throw new NotImplementedException($"Cannot parse default value in column '{column.Name}'"); + } + } else { column.DefaultValue = dataDefaultString; From b693821adb4e7f75a9da8ef5a9d6c56117f93a39 Mon Sep 17 00:00:00 2001 From: JaBistDuNarrisch Date: Thu, 14 Aug 2025 16:41:23 +0200 Subject: [PATCH 12/27] SQLite does not support default BLOB values => throw --- src/Migrator/Providers/Dialect.cs | 7 ++++++- src/Migrator/Providers/Impl/Oracle/OracleDialect.cs | 1 + .../Providers/Impl/SQLite/SQLiteTransformationProvider.cs | 6 +++++- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/Migrator/Providers/Dialect.cs b/src/Migrator/Providers/Dialect.cs index eaad589a..557426e7 100644 --- a/src/Migrator/Providers/Dialect.cs +++ b/src/Migrator/Providers/Dialect.cs @@ -349,8 +349,13 @@ public virtual string Default(object defaultValue) return guidValue; } - else if (defaultValue is DateTime) + else if (defaultValue is DateTime dateTime) { + if (dateTime.Kind != DateTimeKind.Utc) + { + throw new Exception("Use DateTimeKind.Utc for default date time values."); + } + return string.Format("DEFAULT '{0}'", ((DateTime)defaultValue).ToString("yyyy-MM-dd HH:mm:ss")); } else if (defaultValue is string) diff --git a/src/Migrator/Providers/Impl/Oracle/OracleDialect.cs b/src/Migrator/Providers/Impl/Oracle/OracleDialect.cs index da6e958e..c44f2b26 100644 --- a/src/Migrator/Providers/Impl/Oracle/OracleDialect.cs +++ b/src/Migrator/Providers/Impl/Oracle/OracleDialect.cs @@ -160,6 +160,7 @@ public override string Default(object defaultValue) } else if (defaultValue is DateTime dateTime) { + // We use 4 because we have no access data type and therefore no access to the real n in TIMESTAMP(n) in this method. Needs refactoring. var dateTimeString = dateTime.ToString("yyyy-MM-dd HH:mm:ss.ffff"); return $"DEFAULT TO_TIMESTAMP('{dateTimeString}', 'YYYY-MM-DD HH24:MI:SS.FF4')"; } diff --git a/src/Migrator/Providers/Impl/SQLite/SQLiteTransformationProvider.cs b/src/Migrator/Providers/Impl/SQLite/SQLiteTransformationProvider.cs index 0eddfc56..ceef16b1 100644 --- a/src/Migrator/Providers/Impl/SQLite/SQLiteTransformationProvider.cs +++ b/src/Migrator/Providers/Impl/SQLite/SQLiteTransformationProvider.cs @@ -966,7 +966,7 @@ public override Column[] GetColumns(string tableName) dt = defVal.Substring(1, defVal.Length - 2); } - var d = DateTime.ParseExact(dt, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture); + var d = DateTime.ParseExact(dt, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal); column.DefaultValue = d; } } @@ -985,6 +985,10 @@ public override Column[] GetColumns(string tableName) column.DefaultValue = d; } } + else if (column.Type == DbType.Boolean) + { + throw new NotSupportedException("SQLite does not support default values for BLOB columns."); + } } if (pragmaTableInfoItem.Pk > 0) From af56450db5478a47ae26c07c1cad984fb6c9a086 Mon Sep 17 00:00:00 2001 From: JaBistDuNarrisch Date: Thu, 14 Aug 2025 17:13:24 +0200 Subject: [PATCH 13/27] PostgreSQL GetColumns with default columns are green --- ...TransformationProvider_GetColumns_Tests.cs | 29 +++++++++++++++++++ ...onProvider_GetColumns_DefaultValueTests.cs | 18 ++++++++++-- .../Impl/PostgreSQL/PostgreSQLDialect.cs | 2 +- 3 files changed, 45 insertions(+), 4 deletions(-) diff --git a/src/Migrator.Tests/Providers/OracleProvider/OracleTransformationProvider_GetColumns_Tests.cs b/src/Migrator.Tests/Providers/OracleProvider/OracleTransformationProvider_GetColumns_Tests.cs index 990c6d9b..489ff8f0 100644 --- a/src/Migrator.Tests/Providers/OracleProvider/OracleTransformationProvider_GetColumns_Tests.cs +++ b/src/Migrator.Tests/Providers/OracleProvider/OracleTransformationProvider_GetColumns_Tests.cs @@ -1,4 +1,8 @@ +using System; +using System.Data; +using System.Linq; using System.Threading.Tasks; +using DotNetProjects.Migrator.Framework; using Migrator.Tests.Providers.Generic; using NUnit.Framework; @@ -13,4 +17,29 @@ public async Task SetUpAsync() { await BeginOracleTransactionAsync(); } + + /// + /// Since SQLite does not support binary default values in the generic file a separate test is needed for Oracle + /// Find the generic test in the base class. + /// + [Test] + public void GetColumns_Oracle_DefaultValues_Succeeds() + { + // Arrange + const string testTableName = "MyDefaultTestTable"; + const string binaryColumnName1 = "binarycolumn1"; + + // Should be extended by remaining types + Provider.AddTable(testTableName, + new Column(binaryColumnName1, DbType.Binary, defaultValue: new byte[] { 12, 32, 34 }) + ); + + // Act + var columns = Provider.GetColumns(testTableName); + + // Assert + var binarycolumn1 = columns.Single(x => x.Name.Equals(binaryColumnName1, StringComparison.OrdinalIgnoreCase)); + + Assert.That(binarycolumn1.DefaultValue, Is.EqualTo(new byte[] { 12, 32, 34 })); + } } diff --git a/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_GetColumns_DefaultValueTests.cs b/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_GetColumns_DefaultValueTests.cs index 283faf7e..4cc54d9d 100644 --- a/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_GetColumns_DefaultValueTests.cs +++ b/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_GetColumns_DefaultValueTests.cs @@ -1,31 +1,41 @@ using System; using System.Data; using System.Linq; +using System.Threading.Tasks; using DotNetProjects.Migrator.Framework; using Migrator.Tests.Providers.Base; +using Migrator.Tests.Providers.Generic; using NUnit.Framework; namespace Migrator.Tests.Providers.PostgreSQL; [TestFixture] [Category("Postgre")] -public class PostgreSQLTransformationProvider_GetColumns_DefaultTypeTests : PostgreSQLTransformationProviderTestBase +public class PostgreSQLTransformationProvider_GetColumns_DefaultTypeTests : TransformationProvider_GetColumns_GenericTests { + [SetUp] + public async Task SetUpAsync() + { + await BeginPostgreSQLTransactionAsync(); + } + /// /// More tests for GetColumns in /// [Test] - public void GetColumns_DefaultValuesInterval_Succeeds() + public void GetColumns_Postgres_DefaultValues_Succeeds() { // Arrange const string testTableName = "MyDefaultTestTable"; const string intervalColumnName1 = "intervalcolumn1"; const string intervalColumnName2 = "intervalcolumn2"; + const string binaryColumnName1 = "binarycolumn1"; // Should be extended by remaining types Provider.AddTable(testTableName, new Column(intervalColumnName1, MigratorDbType.Interval, defaultValue: new TimeSpan(100000, 3, 4, 5, 666)), - new Column(intervalColumnName2, MigratorDbType.Interval, defaultValue: new TimeSpan(0, 0, 0, 0, 666)) + new Column(intervalColumnName2, MigratorDbType.Interval, defaultValue: new TimeSpan(0, 0, 0, 0, 666)), + new Column(binaryColumnName1, DbType.Binary, defaultValue: new byte[] { 12, 32, 34 }) ); // Act @@ -34,9 +44,11 @@ public void GetColumns_DefaultValuesInterval_Succeeds() // Assert var intervalColumn1 = columns.Single(x => x.Name == intervalColumnName1); var intervalColumn2 = columns.Single(x => x.Name == intervalColumnName2); + var binarycolumn1 = columns.Single(x => x.Name.Equals(binaryColumnName1, StringComparison.OrdinalIgnoreCase)); Assert.That(intervalColumn1.DefaultValue, Is.EqualTo(new TimeSpan(100000, 3, 4, 5, 666))); Assert.That(intervalColumn2.DefaultValue, Is.EqualTo(new TimeSpan(0, 0, 0, 0, 666))); + Assert.That(binarycolumn1.DefaultValue, Is.EqualTo(new byte[] { 12, 32, 34 })); } // 1 will coerce to true on inserts but not for default values in Postgre SQL - same for 0 to false diff --git a/src/Migrator/Providers/Impl/PostgreSQL/PostgreSQLDialect.cs b/src/Migrator/Providers/Impl/PostgreSQL/PostgreSQLDialect.cs index 348ef868..d6e8c2a7 100644 --- a/src/Migrator/Providers/Impl/PostgreSQL/PostgreSQLDialect.cs +++ b/src/Migrator/Providers/Impl/PostgreSQL/PostgreSQLDialect.cs @@ -124,7 +124,7 @@ public override string Default(object defaultValue) else if (defaultValue is byte[] byteArray) { var convertedString = BitConverter.ToString(byteArray).Replace("-", "").ToLower(); - return $"'\\x{convertedString}'"; + return @$"DEFAULT E'\\x{convertedString}'"; } return base.Default(defaultValue); From e0f686c1850421dd0a7fcf2f401202322311e9d9 Mon Sep 17 00:00:00 2001 From: JaBistDuNarrisch Date: Thu, 14 Aug 2025 18:31:36 +0200 Subject: [PATCH 14/27] Added RemoveConstraint --- .../Providers/Impl/SQLite/SQLiteTransformationProvider.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Migrator/Providers/Impl/SQLite/SQLiteTransformationProvider.cs b/src/Migrator/Providers/Impl/SQLite/SQLiteTransformationProvider.cs index ceef16b1..4f626dc5 100644 --- a/src/Migrator/Providers/Impl/SQLite/SQLiteTransformationProvider.cs +++ b/src/Migrator/Providers/Impl/SQLite/SQLiteTransformationProvider.cs @@ -641,6 +641,14 @@ public override void AddUniqueConstraint(string name, string table, params strin RecreateTable(sqliteTableInfo); } + public override void RemoveConstraint(string table, string name) + { + var sqliteTableInfo = GetSQLiteTableInfo(table); + sqliteTableInfo.Uniques.RemoveAll(x => x.Name.Equals(name, StringComparison.OrdinalIgnoreCase)); + + RecreateTable(sqliteTableInfo); + } + public SQLiteTableInfo GetSQLiteTableInfo(string tableName) { var sqliteTable = new SQLiteTableInfo From 17b4e5488c4ee2c016173ea74b21ebf0626b1633 Mon Sep 17 00:00:00 2001 From: JaBistDuNarrisch Date: Thu, 14 Aug 2025 18:31:57 +0200 Subject: [PATCH 15/27] Escape Oracle strings --- src/Migrator/Providers/Impl/Oracle/OracleDialect.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Migrator/Providers/Impl/Oracle/OracleDialect.cs b/src/Migrator/Providers/Impl/Oracle/OracleDialect.cs index c44f2b26..7c3a20ad 100644 --- a/src/Migrator/Providers/Impl/Oracle/OracleDialect.cs +++ b/src/Migrator/Providers/Impl/Oracle/OracleDialect.cs @@ -166,6 +166,7 @@ public override string Default(object defaultValue) } else if (defaultValue is string stringValue) { + stringValue = stringValue.Replace("'", "''"); return $"DEFAULT '{stringValue}'"; } else if (defaultValue is byte[] byteArray) From 93d2986304dec722bb299cbfc4a0cd23b9664fce Mon Sep 17 00:00:00 2001 From: JaBistDuNarrisch Date: Thu, 14 Aug 2025 18:32:20 +0200 Subject: [PATCH 16/27] Create new database for Postgre --- .../Base/TransformationProviderBase.cs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/Migrator.Tests/Providers/Base/TransformationProviderBase.cs b/src/Migrator.Tests/Providers/Base/TransformationProviderBase.cs index 9dbb2ee4..f16ff560 100644 --- a/src/Migrator.Tests/Providers/Base/TransformationProviderBase.cs +++ b/src/Migrator.Tests/Providers/Base/TransformationProviderBase.cs @@ -88,18 +88,27 @@ protected async Task BeginOracleTransactionAsync() protected async Task BeginPostgreSQLTransactionAsync() { + using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10)); var configReader = new ConfigurationReader(); - var connectionString = configReader.GetDatabaseConnectionConfigById(DatabaseConnectionConfigIds.PostgreSQL) - ?.ConnectionString; + + var databaseConnectionConfig = configReader.GetDatabaseConnectionConfigById(DatabaseConnectionConfigIds.PostgreSQL); + + var connectionString = databaseConnectionConfig?.ConnectionString; if (string.IsNullOrEmpty(connectionString)) { - throw new IgnoreException("No Postgre ConnectionString is Set."); + throw new IgnoreException("No Postgre SQL connection string is set."); } DbProviderFactories.RegisterFactory("Npgsql", () => Npgsql.NpgsqlFactory.Instance); - Provider = new PostgreSQLTransformationProvider(new PostgreSQLDialect(), connectionString, null, "default", "Npgsql"); + using var container = new Container(); + container.RegisterDatabaseIntegrationTestService(); + var databaseIntegrationTestServiceFactory = container.Resolve(); + var postgreIntegrationTestService = databaseIntegrationTestServiceFactory.Create(DatabaseProviderType.Postgres); + var databaseInfo = await postgreIntegrationTestService.CreateTestDatabaseAsync(databaseConnectionConfig, cts.Token); + + Provider = new PostgreSQLTransformationProvider(new PostgreSQLDialect(), databaseInfo.DatabaseConnectionConfig.ConnectionString, null, "default", "Npgsql"); Provider.BeginTransaction(); await Task.CompletedTask; From 722e1f8397ee94e78f397b9f36a0348e28afedbe Mon Sep 17 00:00:00 2001 From: JaBistDuNarrisch Date: Thu, 14 Aug 2025 18:32:35 +0200 Subject: [PATCH 17/27] Refactoring test --- .../Generic/TransformationProviderGenericMiscTests.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/Migrator.Tests/Providers/Generic/TransformationProviderGenericMiscTests.cs b/src/Migrator.Tests/Providers/Generic/TransformationProviderGenericMiscTests.cs index 5647cba2..65971c7e 100644 --- a/src/Migrator.Tests/Providers/Generic/TransformationProviderGenericMiscTests.cs +++ b/src/Migrator.Tests/Providers/Generic/TransformationProviderGenericMiscTests.cs @@ -43,14 +43,19 @@ public void TableCanBeAdded() [Test] public void GetTablesWorks() { - foreach (var name in Provider.GetTables()) + var tables = Provider.GetTables(); + + foreach (var name in tables) { Provider.Logger.Log("Table: {0}", name); } - Assert.That(1, Is.EqualTo(Provider.GetTables().Length)); + Assert.That(1, Is.EqualTo(tables.Length)); AddTable(); - Assert.That(2, Is.EqualTo(Provider.GetTables().Length)); + + tables = Provider.GetTables(); + + Assert.That(2, Is.EqualTo(tables.Length)); } [Test] From 5c95edef1476c991d63f7e707417b1e60e7a7e13 Mon Sep 17 00:00:00 2001 From: JaBistDuNarrisch Date: Fri, 15 Aug 2025 10:16:58 +0200 Subject: [PATCH 18/27] Added GetCheckConstraints, RemoveForeignKey and much more... --- ...mationProviderGenericMiscConstraintBase.cs | 17 +-- .../SQLiteTransformationProviderTestBase.cs | 2 + ...mationProvider_GetCheckConstraintsTests.cs | 61 +++++++++ src/Migrator/Framework/CheckConstraint.cs | 26 ++++ .../Oracle/OracleTransformationProvider.cs | 4 + .../Impl/SQLite/Models/SQLiteTableInfo.cs | 5 + .../SQLite/SQLiteTransformationProvider.cs | 123 +++++++++++++++++- 7 files changed, 223 insertions(+), 15 deletions(-) create mode 100644 src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_GetCheckConstraintsTests.cs create mode 100644 src/Migrator/Framework/CheckConstraint.cs diff --git a/src/Migrator.Tests/Providers/Generic/TransformationProviderGenericMiscConstraintBase.cs b/src/Migrator.Tests/Providers/Generic/TransformationProviderGenericMiscConstraintBase.cs index f93b2154..15a05b50 100644 --- a/src/Migrator.Tests/Providers/Generic/TransformationProviderGenericMiscConstraintBase.cs +++ b/src/Migrator.Tests/Providers/Generic/TransformationProviderGenericMiscConstraintBase.cs @@ -131,8 +131,8 @@ public void ConstraintExist() public void AddTableWithCompoundPrimaryKey() { Provider.AddTable("Test", - new Column("PersonId", DbType.Int32, ColumnProperty.PrimaryKey), - new Column("AddressId", DbType.Int32, ColumnProperty.PrimaryKey) + new Column("PersonId", DbType.Int32, ColumnProperty.PrimaryKey), + new Column("AddressId", DbType.Int32, ColumnProperty.PrimaryKey) ); Assert.That(Provider.TableExists("Test"), Is.True, "Table doesn't exist"); @@ -142,14 +142,15 @@ public void AddTableWithCompoundPrimaryKey() [Test] public void AddTableWithCompoundPrimaryKeyShouldKeepNullForOtherProperties() { - Provider.AddTable("Test", - new Column("PersonId", DbType.Int32, ColumnProperty.PrimaryKey), - new Column("AddressId", DbType.Int32, ColumnProperty.PrimaryKey), - new Column("Name", DbType.String, 30, ColumnProperty.Null) - ); + var testTableName = "Test"; + + Provider.AddTable(testTableName, + new Column("PersonId", DbType.Int32, ColumnProperty.PrimaryKey), + new Column("AddressId", DbType.Int32, ColumnProperty.PrimaryKey), + new Column("Name", DbType.String, 30, ColumnProperty.Null) + ); Assert.That(Provider.TableExists("Test"), Is.True, "Table doesn't exist"); - Assert.That(Provider.PrimaryKeyExists("Test", "PK_Test"), Is.True, "Constraint doesn't exist"); var column = Provider.GetColumnByName("Test", "Name"); diff --git a/src/Migrator.Tests/Providers/SQLite/Base/SQLiteTransformationProviderTestBase.cs b/src/Migrator.Tests/Providers/SQLite/Base/SQLiteTransformationProviderTestBase.cs index 3c6ffa6a..4e3d5414 100644 --- a/src/Migrator.Tests/Providers/SQLite/Base/SQLiteTransformationProviderTestBase.cs +++ b/src/Migrator.Tests/Providers/SQLite/Base/SQLiteTransformationProviderTestBase.cs @@ -21,6 +21,8 @@ public async Task SetUpAsync() Provider = new SQLiteTransformationProvider(new SQLiteDialect(), connectionString, "default", null); Provider.BeginTransaction(); + AddDefaultTable(); + await Task.CompletedTask; } } diff --git a/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_GetCheckConstraintsTests.cs b/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_GetCheckConstraintsTests.cs new file mode 100644 index 00000000..64815fb6 --- /dev/null +++ b/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_GetCheckConstraintsTests.cs @@ -0,0 +1,61 @@ +using System; +using System.Data.SQLite; +using DotNetProjects.Migrator.Framework; +using DotNetProjects.Migrator.Providers.Impl.SQLite; +using Migrator.Tests.Providers.SQLite.Base; +using NUnit.Framework; + +namespace Migrator.Tests.Providers.SQLite; + +[TestFixture] +[Category("SQLite")] +public class SQLiteTransformationProvider_GetCheckConstraintsTests : SQLiteTransformationProviderTestBase +{ + [Test] + public void GetCheckConstraints_AddCheckConstraintsViaAddTable_CreatesTableCorrectly() + { + const string tableName = "MyTableName"; + const string columnName = "MyColumnName"; + const string checkConstraint1 = "MyCheckConstraint1"; + const string checkConstraint2 = "MyCheckConstraint2"; + + // Arrange/Act + Provider.AddTable(tableName, + new Column(columnName, System.Data.DbType.Int32), + new CheckConstraint(checkConstraint1, $"{columnName} > 10"), + new CheckConstraint(checkConstraint2, $"{columnName} < 100") + ); + + var checkConstraints = ((SQLiteTransformationProvider)Provider).GetCheckConstraints(tableName); + + // Assert + Assert.That(checkConstraints[0].Name, Is.EqualTo(checkConstraint1)); + Assert.That(checkConstraints[0].CheckConstraintString, Is.EqualTo($"{columnName} > 10")); + + Assert.That(checkConstraints[1].Name, Is.EqualTo(checkConstraint2)); + Assert.That(checkConstraints[1].CheckConstraintString, Is.EqualTo($"{columnName} < 100")); + + Provider.Insert(tableName, [columnName], [11]); + Assert.Throws(() => Provider.Insert(tableName, [columnName], [1])); + Assert.Throws(() => Provider.Insert(tableName, [columnName], [200])); + + var createScript = ((SQLiteTransformationProvider)Provider).GetSqlCreateTableScript(tableName); + Assert.That(createScript, Is.EqualTo("CREATE TABLE MyTableName (MyColumnName INTEGER NULL, CONSTRAINT MyCheckConstraint1 CHECK (MyColumnName > 10), CONSTRAINT MyCheckConstraint2 CHECK (MyColumnName < 100))")); + } + + [Test] + public void CheckForeignKeyIntegrity_IntegrityOk_ReturnsTrue() + { + // Arrange + AddTableWithPrimaryKey(); + Provider.ExecuteNonQuery("INSERT INTO Test (Id, name) VALUES (1, 'my name')"); + Provider.ExecuteNonQuery("INSERT INTO TestTwo (TestId) VALUES (1)"); + Provider.AddForeignKey(name: "FKName", childTable: "TestTwo", childColumn: "TestId", parentTable: "Test", parentColumn: "Id", constraint: ForeignKeyConstraintType.Cascade); + + // Act + var result = ((SQLiteTransformationProvider)Provider).CheckForeignKeyIntegrity(); + + // Assert + Assert.That(result, Is.True); + } +} diff --git a/src/Migrator/Framework/CheckConstraint.cs b/src/Migrator/Framework/CheckConstraint.cs new file mode 100644 index 00000000..d682fa9a --- /dev/null +++ b/src/Migrator/Framework/CheckConstraint.cs @@ -0,0 +1,26 @@ +namespace DotNetProjects.Migrator.Framework; + +/// +/// Currently only used for SQLite +/// +public class CheckConstraint : IDbField +{ + public CheckConstraint() + { } + + public CheckConstraint(string name, string checkConstraintText) + { + CheckConstraintString = checkConstraintText; + Name = name; + } + + /// + /// Gets or sets the CheckConstraintString. Add it without the braces they will be added by the migrator. + /// + public string CheckConstraintString { get; set; } + + /// + /// Gets or sets the name of the CHECK constraint. + /// + public string Name { get; set; } +} diff --git a/src/Migrator/Providers/Impl/Oracle/OracleTransformationProvider.cs b/src/Migrator/Providers/Impl/Oracle/OracleTransformationProvider.cs index 728b5978..337250d1 100644 --- a/src/Migrator/Providers/Impl/Oracle/OracleTransformationProvider.cs +++ b/src/Migrator/Providers/Impl/Oracle/OracleTransformationProvider.cs @@ -547,6 +547,10 @@ public override Column[] GetColumns(string table) { column.MigratorDbType = MigratorDbType.Boolean; } + else if (dataTypeString == "NCLOB") + { + column.MigratorDbType = MigratorDbType.String; + } else { throw new NotImplementedException(); diff --git a/src/Migrator/Providers/Impl/SQLite/Models/SQLiteTableInfo.cs b/src/Migrator/Providers/Impl/SQLite/Models/SQLiteTableInfo.cs index e2512c77..c839b034 100644 --- a/src/Migrator/Providers/Impl/SQLite/Models/SQLiteTableInfo.cs +++ b/src/Migrator/Providers/Impl/SQLite/Models/SQLiteTableInfo.cs @@ -34,4 +34,9 @@ public class SQLiteTableInfo /// Gets or sets the unique definitions. /// public List Uniques { get; set; } = []; + + /// + /// Gets or sets the check constraint definitions. + /// + public List CheckConstraints { get; set; } = []; } \ No newline at end of file diff --git a/src/Migrator/Providers/Impl/SQLite/SQLiteTransformationProvider.cs b/src/Migrator/Providers/Impl/SQLite/SQLiteTransformationProvider.cs index 4f626dc5..095d9391 100644 --- a/src/Migrator/Providers/Impl/SQLite/SQLiteTransformationProvider.cs +++ b/src/Migrator/Providers/Impl/SQLite/SQLiteTransformationProvider.cs @@ -7,6 +7,7 @@ using System.Linq; using System.Text; using System.Text.RegularExpressions; +using System.Xml.Linq; using ForeignKeyConstraint = DotNetProjects.Migrator.Framework.ForeignKeyConstraint; using Index = DotNetProjects.Migrator.Framework.Index; @@ -108,7 +109,7 @@ public string GetSqlCreateTableScript(string table) { if (reader.Read()) { - sqlCreateTableScript = (string)reader[0]; + sqlCreateTableScript = reader.IsDBNull(0) ? null : (string)reader[0]; } } @@ -340,8 +341,10 @@ public DbType ExtractTypeFromColumnDef(string columnDef) public override void RemoveForeignKey(string table, string name) { - //Check the impl... - return; + var sqliteTableInfo = GetSQLiteTableInfo(table); + sqliteTableInfo.ForeignKeys.RemoveAll(x => x.Name.Equals(name, StringComparison.OrdinalIgnoreCase)); + + RecreateTable(sqliteTableInfo); } public string[] GetCreateIndexSqlStrings(string table) @@ -404,6 +407,11 @@ public override void RemoveColumn(string tableName, string column) var sqliteInfoMainTable = GetSQLiteTableInfo(tableName); + if (sqliteInfoMainTable.CheckConstraints.Any(x => x.CheckConstraintString.Equals(column, StringComparison.OrdinalIgnoreCase))) + { + throw new Exception("A check constraint contains the column you want to remove. Remove the check constraint first"); + } + if (!sqliteInfoMainTable.ColumnMappings.Any(x => x.OldName == column)) { throw new MigrationException("Column not found"); @@ -645,19 +653,26 @@ public override void RemoveConstraint(string table, string name) { var sqliteTableInfo = GetSQLiteTableInfo(table); sqliteTableInfo.Uniques.RemoveAll(x => x.Name.Equals(name, StringComparison.OrdinalIgnoreCase)); + sqliteTableInfo.CheckConstraints.RemoveAll(x => x.Name.Equals(name, StringComparison.OrdinalIgnoreCase)); RecreateTable(sqliteTableInfo); } public SQLiteTableInfo GetSQLiteTableInfo(string tableName) { + if (!TableExists(tableName)) + { + return null; + } + var sqliteTable = new SQLiteTableInfo { TableNameMapping = new MappingInfo { OldName = tableName, NewName = tableName }, Columns = GetColumns(tableName).ToList(), ForeignKeys = GetForeignKeyConstraints(tableName).ToList(), Indexes = GetIndexes(tableName).ToList(), - Uniques = GetUniques(tableName).ToList() + Uniques = GetUniques(tableName).ToList(), + CheckConstraints = GetCheckConstraints(tableName) }; sqliteTable.ColumnMappings = sqliteTable.Columns @@ -715,6 +730,7 @@ public void RecreateTable(SQLiteTableInfo sqliteTableInfo) var foreignKeyDbFields = sqliteTableInfo.ForeignKeys.Cast(); var indexDbFields = sqliteTableInfo.Indexes.Cast(); var uniqueDbFields = sqliteTableInfo.Uniques.Cast(); + var checkConstraintDbFields = sqliteTableInfo.CheckConstraints.Cast(); var dbFields = columnDbFields.Concat(foreignKeyDbFields) .Concat(uniqueDbFields) @@ -848,6 +864,11 @@ public override List GetDatabases() public override bool ConstraintExists(string table, string name) { + if (!TableExists(table)) + { + throw new Exception($"Table '{table}' does not exist."); + } + var constraintNames = GetConstraints(table); var exists = constraintNames.Any(x => x.Equals(name, StringComparison.OrdinalIgnoreCase)); @@ -857,6 +878,11 @@ public override bool ConstraintExists(string table, string name) public override string[] GetConstraints(string table) { + if (!TableExists(table)) + { + throw new Exception($"Table '{table}' does not exist."); + } + var sqliteInfo = GetSQLiteTableInfo(table); var foreignKeyNames = sqliteInfo.ForeignKeys @@ -1131,6 +1157,8 @@ public override void AddTable(string name, string engine, params IDbField[] fiel stringBuilder.Append(string.Format(", PRIMARY KEY ({0})", string.Join(", ", pks.ToArray()))); } + + // Uniques var uniques = fields.Where(x => x is Unique).Cast().ToArray(); foreach (var u in uniques) @@ -1148,6 +1176,7 @@ public override void AddTable(string name, string engine, params IDbField[] fiel stringBuilder.Append($" UNIQUE ({uniqueColumnsCommaSeparated})"); } + // Foreign keys var foreignKeys = fields.Where(x => x is ForeignKeyConstraint).Cast().ToArray(); List foreignKeyStrings = []; @@ -1166,12 +1195,25 @@ public override void AddTable(string name, string engine, params IDbField[] fiel foreignKeyStrings.Add($"CONSTRAINT {fk.Name} FOREIGN KEY ({sourceColumnNamesQuotedString}) REFERENCES {parentTableNameQuoted}({parentColumnNamesQuotedString})"); } - if (foreignKeyStrings.Count != 0) + if (foreignKeyStrings.Count > 0) { stringBuilder.Append(", "); stringBuilder.Append(string.Join(", ", foreignKeyStrings)); } + // Check Constraints + var checkConstraints = fields.Where(x => x is CheckConstraint).OfType().ToArray(); + List checkConstraintStrings = []; + + foreach (var checkConstraint in checkConstraints) + { + checkConstraintStrings.Add($"CONSTRAINT {checkConstraint.Name} CHECK ({checkConstraint.CheckConstraintString})"); + } + + if (checkConstraintStrings.Count > 0) + { + stringBuilder.Append($", {string.Join(", ", checkConstraintStrings)}"); + } stringBuilder.Append(')'); @@ -1251,6 +1293,11 @@ public override void RemoveAllIndexes(string tableName) public List GetUniques(string tableName) { + if (!TableExists(tableName)) + { + throw new Exception($"Table '{tableName}' does not exist."); + } + var regEx = new Regex(@"(?<=,)\s*(CONSTRAINT\s+\w+\s+)?UNIQUE\s*\(\s*[\w\s,]+\s*\)\s*(?=,|\s*\))"); var regExConstraintName = new Regex(@"(?<=CONSTRAINT\s+)\w+(?=\s+)"); var regExParenthesis = new Regex(@"(?<=\().+(?=\))"); @@ -1285,10 +1332,20 @@ public List GetUniques(string tableName) var createScript = GetSqlCreateTableScript(tableName); - var matches = regEx.Matches(createScript).Cast().Where(x => x.Success).Select(x => x.Value.Trim()).ToList(); + var matches = regEx.Matches(createScript); + if (matches.Count == 0) + { + return []; + } + + var constraintNames = matches + .OfType() + .Where(x => x.Success && !string.IsNullOrWhiteSpace(x.Value)) + .Select(x => x.Value.Trim()) + .ToList(); // We can only use the ones containing a starting with CONSTRAINT - var matchesHavingName = matches.Where(x => x.StartsWith("CONSTRAINT")).ToList(); + var matchesHavingName = constraintNames.Where(x => x.StartsWith("CONSTRAINT")).ToList(); foreach (var constraintString in matchesHavingName) { @@ -1397,6 +1454,58 @@ public List GetPragmaTableInfoItems(string tableNameNotQuot return pragmaTableInfoItems; } + public List GetCheckConstraints(string tableName) + { + if (!TableExists(tableName)) + { + throw new Exception($"Table '{tableName}' does not exist."); + } + + var checkConstraintRegex = new Regex(@"(?<=,)[^,]+\s+[^,]+check[^,]+(?=[,|\)])", RegexOptions.IgnoreCase); + var braceContentRegex = new Regex(@"(?<=^\().+(?=\)$)"); + + var script = GetSqlCreateTableScript(tableName); + + var matches = checkConstraintRegex.Matches(script); + + if (matches == null) + { + return []; + } + + var checkStrings = matches.OfType() + .Where(x => x.Success) + .Select(x => x.Value) + .ToList(); + + List checkConstraints = []; + + foreach (var checkString in checkStrings) + { + var splitted = checkString.Trim().Split(' ') + .Select(x => x.Trim()) + .ToList(); + + if (!splitted[0].Equals("CONSTRAINT", StringComparison.OrdinalIgnoreCase) || !splitted[2].Equals("CHECK", StringComparison.OrdinalIgnoreCase)) + { + throw new Exception($"Cannot parse check constraint in table {tableName}"); + } + + var checkConstraintStringWithBraces = string.Join(" ", splitted.Skip(3)).Trim(); + var checkConstraintString = braceContentRegex.Match(checkConstraintStringWithBraces); + + var checkConstraint = new CheckConstraint + { + Name = splitted[1], + CheckConstraintString = checkConstraintString.Value + }; + + checkConstraints.Add(checkConstraint); + } + + return checkConstraints; + } + protected override void ConfigureParameterWithValue(IDbDataParameter parameter, int index, object value) { if (value is ushort) From 03febdd7e1d4bbed104df8dbf78ff894f9b3492c Mon Sep 17 00:00:00 2001 From: JaBistDuNarrisch Date: Mon, 18 Aug 2025 09:29:21 +0200 Subject: [PATCH 19/27] Added Contains compatible with old .NET version --- .../Framework/Extensions/LinqExtensions.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 src/Migrator/Framework/Extensions/LinqExtensions.cs diff --git a/src/Migrator/Framework/Extensions/LinqExtensions.cs b/src/Migrator/Framework/Extensions/LinqExtensions.cs new file mode 100644 index 00000000..d8bdcb4d --- /dev/null +++ b/src/Migrator/Framework/Extensions/LinqExtensions.cs @@ -0,0 +1,18 @@ +using System; + +namespace DotNetProjects.Migrator.Framework.Extensions; + +public static class LinqExtensions +{ + /// + /// Is equal to the Contains method in .NET 9. Please remove it after .NET upgrade. + /// + /// + /// + /// + /// + public static bool Contains(this string source, string toBeChecked, StringComparison stringComparison) + { + return source?.IndexOf(toBeChecked, stringComparison) >= 0; + } +} \ No newline at end of file From 3f6141745764dc01d6907a93ff54b1358acf07a8 Mon Sep 17 00:00:00 2001 From: JaBistDuNarrisch Date: Mon, 18 Aug 2025 09:32:27 +0200 Subject: [PATCH 20/27] Added checkConstraint test --- ...mationProvider_GetCheckConstraintsTests.cs | 17 ----------------- ...ransformationProvider_RemoveColumnTests.cs | 19 +++++++++++++++++++ .../SQLite/SQLiteTransformationProvider.cs | 8 +++++--- 3 files changed, 24 insertions(+), 20 deletions(-) diff --git a/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_GetCheckConstraintsTests.cs b/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_GetCheckConstraintsTests.cs index 64815fb6..c9e20eca 100644 --- a/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_GetCheckConstraintsTests.cs +++ b/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_GetCheckConstraintsTests.cs @@ -1,4 +1,3 @@ -using System; using System.Data.SQLite; using DotNetProjects.Migrator.Framework; using DotNetProjects.Migrator.Providers.Impl.SQLite; @@ -42,20 +41,4 @@ public void GetCheckConstraints_AddCheckConstraintsViaAddTable_CreatesTableCorre var createScript = ((SQLiteTransformationProvider)Provider).GetSqlCreateTableScript(tableName); Assert.That(createScript, Is.EqualTo("CREATE TABLE MyTableName (MyColumnName INTEGER NULL, CONSTRAINT MyCheckConstraint1 CHECK (MyColumnName > 10), CONSTRAINT MyCheckConstraint2 CHECK (MyColumnName < 100))")); } - - [Test] - public void CheckForeignKeyIntegrity_IntegrityOk_ReturnsTrue() - { - // Arrange - AddTableWithPrimaryKey(); - Provider.ExecuteNonQuery("INSERT INTO Test (Id, name) VALUES (1, 'my name')"); - Provider.ExecuteNonQuery("INSERT INTO TestTwo (TestId) VALUES (1)"); - Provider.AddForeignKey(name: "FKName", childTable: "TestTwo", childColumn: "TestId", parentTable: "Test", parentColumn: "Id", constraint: ForeignKeyConstraintType.Cascade); - - // Act - var result = ((SQLiteTransformationProvider)Provider).CheckForeignKeyIntegrity(); - - // Assert - Assert.That(result, Is.True); - } } diff --git a/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_RemoveColumnTests.cs b/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_RemoveColumnTests.cs index 9c089e1c..302d23fc 100644 --- a/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_RemoveColumnTests.cs +++ b/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_RemoveColumnTests.cs @@ -265,4 +265,23 @@ public void RemoveColumn_HavingAForeignKeyPointingFromTableToParentAndForeignKey var valid = ((SQLiteTransformationProvider)Provider).CheckForeignKeyIntegrity(); Assert.That(valid, Is.True); } + + [Test] + public void RemoveColumn_ColumnExistsInCheckConstraintString_Throws() + { + const string tableName = "MyTableName"; + const string columnName = "MyColumnName"; + const string checkConstraint1 = "MyCheckConstraint1"; + + // Arrange + Provider.AddTable(tableName, + new Column(columnName, System.Data.DbType.Int32), + new CheckConstraint(checkConstraint1, $"{columnName} > 10") + ); + + var checkConstraints = ((SQLiteTransformationProvider)Provider).GetCheckConstraints(tableName); + + // Act/Assert + Assert.Throws(() => Provider.RemoveColumn(tableName, columnName)); + } } \ No newline at end of file diff --git a/src/Migrator/Providers/Impl/SQLite/SQLiteTransformationProvider.cs b/src/Migrator/Providers/Impl/SQLite/SQLiteTransformationProvider.cs index 095d9391..741ada02 100644 --- a/src/Migrator/Providers/Impl/SQLite/SQLiteTransformationProvider.cs +++ b/src/Migrator/Providers/Impl/SQLite/SQLiteTransformationProvider.cs @@ -7,9 +7,9 @@ using System.Linq; using System.Text; using System.Text.RegularExpressions; -using System.Xml.Linq; using ForeignKeyConstraint = DotNetProjects.Migrator.Framework.ForeignKeyConstraint; using Index = DotNetProjects.Migrator.Framework.Index; +using DotNetProjects.Migrator.Framework.Extensions; namespace DotNetProjects.Migrator.Providers.Impl.SQLite; @@ -407,9 +407,11 @@ public override void RemoveColumn(string tableName, string column) var sqliteInfoMainTable = GetSQLiteTableInfo(tableName); - if (sqliteInfoMainTable.CheckConstraints.Any(x => x.CheckConstraintString.Equals(column, StringComparison.OrdinalIgnoreCase))) + var checkConstraints = sqliteInfoMainTable.CheckConstraints; + + if (checkConstraints.Any(x => x.CheckConstraintString.Contains(column, StringComparison.OrdinalIgnoreCase))) { - throw new Exception("A check constraint contains the column you want to remove. Remove the check constraint first"); + throw new MigrationException("A check constraint contains the column you want to remove. Remove the check constraint first"); } if (!sqliteInfoMainTable.ColumnMappings.Any(x => x.OldName == column)) From ddf50151bb31a5fd40e52f32e4a5b685fba339b6 Mon Sep 17 00:00:00 2001 From: JaBistDuNarrisch Date: Mon, 18 Aug 2025 09:41:31 +0200 Subject: [PATCH 21/27] Added check for CheckConstraints in AddTable in TransformationProvider --- src/Migrator/Providers/TransformationProvider.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Migrator/Providers/TransformationProvider.cs b/src/Migrator/Providers/TransformationProvider.cs index b06d0a25..d36c8684 100644 --- a/src/Migrator/Providers/TransformationProvider.cs +++ b/src/Migrator/Providers/TransformationProvider.cs @@ -380,6 +380,11 @@ public virtual void AddView(string name, string tableName, params IViewElement[] /// public virtual void AddTable(string name, params IDbField[] columns) { + if (columns.Any(x => x is CheckConstraint)) + { + throw new MigrationException($"{nameof(CheckConstraint)}s are currently supported in SQLite only."); + } + // Most databases don't have the concept of a storage engine, so default is to not use it. AddTable(name, null, columns); } From 4547883ae023061f818d2fc0abcd87944230136a Mon Sep 17 00:00:00 2001 From: JaBistDuNarrisch Date: Mon, 18 Aug 2025 10:15:41 +0200 Subject: [PATCH 22/27] Moved getconstraints test from generic tests to Oracle, SQL Server and Postgre (the servers we support currently) --- ...rmationProvider_GetColumns_GenericTests.cs | 82 ------------------- ...TransformationProvider_GetColumns_Tests.cs | 71 +++++++++++++++- ...onProvider_GetColumns_DefaultValueTests.cs | 70 +++++++++++++++- ...TransformationProvider_GetColumns_Tests.cs | 4 +- ...rTransformationProvider_GetColumnsTests.cs | 4 +- ...eTransformationProvider_GetColumnsTests.cs | 4 +- .../SqlServerTransformationProvider.cs | 4 + 7 files changed, 149 insertions(+), 90 deletions(-) delete mode 100644 src/Migrator.Tests/Providers/Generic/TransformationProvider_GetColumns_GenericTests.cs diff --git a/src/Migrator.Tests/Providers/Generic/TransformationProvider_GetColumns_GenericTests.cs b/src/Migrator.Tests/Providers/Generic/TransformationProvider_GetColumns_GenericTests.cs deleted file mode 100644 index ee7e3760..00000000 --- a/src/Migrator.Tests/Providers/Generic/TransformationProvider_GetColumns_GenericTests.cs +++ /dev/null @@ -1,82 +0,0 @@ -using System; -using System.Data; -using System.Linq; -using DotNetProjects.Migrator.Framework; -using Migrator.Tests.Providers.Base; -using NUnit.Framework; - -namespace Migrator.Tests.Providers.Generic; - -/// -/// Base class for provider tests. -/// -public abstract class TransformationProvider_GetColumns_GenericTests : TransformationProviderBase -{ - [Test] - public void GetColumns_DefaultValues_Succeeds() - { - // Arrange - var dateTimeDefaultValue = new DateTime(2000, 1, 2, 3, 4, 5, DateTimeKind.Utc); - var guidDefaultValue = Guid.NewGuid(); - var decimalDefaultValue = 14.56565m; - - const string testTableName = "MyDefaultTestTable"; - - const string dateTimeColumnName1 = "datetimecolumn1"; - const string dateTimeColumnName2 = "datetimecolumn2"; - const string decimalColumnName1 = "decimalcolumn"; - const string guidColumnName1 = "guidcolumn1"; - const string booleanColumnName1 = "booleancolumn1"; - const string int32ColumnName1 = "int32column1"; - const string int64ColumnName1 = "int64column1"; - const string int64ColumnName2 = "int64column2"; - const string stringColumnName1 = "stringcolumn1"; - const string binaryColumnName1 = "binarycolumn1"; - const string doubleColumnName1 = "doublecolumn1"; - - // Should be extended by remaining types - Provider.AddTable(testTableName, - new Column(dateTimeColumnName1, DbType.DateTime, dateTimeDefaultValue), - new Column(dateTimeColumnName2, DbType.DateTime2, dateTimeDefaultValue), - new Column(decimalColumnName1, DbType.Decimal, decimalDefaultValue), - new Column(guidColumnName1, DbType.Guid, guidDefaultValue), - - // other boolean default values are tested in another test - new Column(booleanColumnName1, DbType.Boolean, true), - - new Column(int32ColumnName1, DbType.Int32, defaultValue: 43), - new Column(int64ColumnName1, DbType.Int64, defaultValue: 88), - new Column(int64ColumnName2, DbType.Int64, defaultValue: 0), - new Column(stringColumnName1, DbType.String, defaultValue: "Hello"), - new Column(binaryColumnName1, DbType.Binary, defaultValue: new byte[] { 12, 32, 34 }), - new Column(doubleColumnName1, DbType.Double, defaultValue: 84.874596567) { Precision = 19, Scale = 10 } - ); - - // Act - var columns = Provider.GetColumns(testTableName); - - // Assert - var dateTimeColumn1 = columns.Single(x => x.Name.Equals(dateTimeColumnName1, StringComparison.OrdinalIgnoreCase)); - var dateTimeColumn2 = columns.Single(x => x.Name.Equals(dateTimeColumnName2, StringComparison.OrdinalIgnoreCase)); - var decimalColumn1 = columns.Single(x => x.Name.Equals(decimalColumnName1, StringComparison.OrdinalIgnoreCase)); - var guidColumn1 = columns.Single(x => x.Name.Equals(guidColumnName1, StringComparison.OrdinalIgnoreCase)); - var booleanColumn1 = columns.Single(x => x.Name.Equals(booleanColumnName1, StringComparison.OrdinalIgnoreCase)); - var int32Column1 = columns.Single(x => x.Name.Equals(int32ColumnName1, StringComparison.OrdinalIgnoreCase)); - var int64Column1 = columns.Single(x => x.Name.Equals(int64ColumnName1, StringComparison.OrdinalIgnoreCase)); - var int64Column2 = columns.Single(x => x.Name.Equals(int64ColumnName2, StringComparison.OrdinalIgnoreCase)); - var stringColumn1 = columns.Single(x => x.Name.Equals(stringColumnName1, StringComparison.OrdinalIgnoreCase)); - var binarycolumn1 = columns.Single(x => x.Name.Equals(binaryColumnName1, StringComparison.OrdinalIgnoreCase)); - var doubleColumn1 = columns.Single(x => x.Name.Equals(doubleColumnName1, StringComparison.OrdinalIgnoreCase)); - - Assert.That(dateTimeColumn1.DefaultValue, Is.EqualTo(dateTimeDefaultValue)); - Assert.That(dateTimeColumn2.DefaultValue, Is.EqualTo(dateTimeDefaultValue)); - Assert.That(decimalColumn1.DefaultValue, Is.EqualTo(decimalDefaultValue)); - Assert.That(guidColumn1.DefaultValue, Is.EqualTo(guidDefaultValue)); - Assert.That(booleanColumn1.DefaultValue, Is.True); - Assert.That(int32Column1.DefaultValue, Is.EqualTo(43)); - Assert.That(int64Column1.DefaultValue, Is.EqualTo(88)); - Assert.That(stringColumn1.DefaultValue, Is.EqualTo("Hello")); - Assert.That(binarycolumn1.DefaultValue, Is.EqualTo(new byte[] { 12, 32, 34 })); - Assert.That(doubleColumn1.DefaultValue, Is.EqualTo(84.874596567)); - } -} diff --git a/src/Migrator.Tests/Providers/OracleProvider/OracleTransformationProvider_GetColumns_Tests.cs b/src/Migrator.Tests/Providers/OracleProvider/OracleTransformationProvider_GetColumns_Tests.cs index 489ff8f0..7993fbba 100644 --- a/src/Migrator.Tests/Providers/OracleProvider/OracleTransformationProvider_GetColumns_Tests.cs +++ b/src/Migrator.Tests/Providers/OracleProvider/OracleTransformationProvider_GetColumns_Tests.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Threading.Tasks; using DotNetProjects.Migrator.Framework; +using Migrator.Tests.Providers.Base; using Migrator.Tests.Providers.Generic; using NUnit.Framework; @@ -10,7 +11,7 @@ namespace Migrator.Tests.Providers.OracleProvider; [TestFixture] [Category("Oracle")] -public class OracleTransformationProvider_GetColumns_Tests : TransformationProvider_GetColumns_GenericTests +public class OracleTransformationProvider_GetColumns_Tests : TransformationProviderBase { [SetUp] public async Task SetUpAsync() @@ -42,4 +43,72 @@ public void GetColumns_Oracle_DefaultValues_Succeeds() Assert.That(binarycolumn1.DefaultValue, Is.EqualTo(new byte[] { 12, 32, 34 })); } + + [Test] + public void GetColumns_DefaultValues_Succeeds() + { + // Arrange + var dateTimeDefaultValue = new DateTime(2000, 1, 2, 3, 4, 5, DateTimeKind.Utc); + var guidDefaultValue = Guid.NewGuid(); + var decimalDefaultValue = 14.56565m; + + const string testTableName = "MyDefaultTestTable"; + + const string dateTimeColumnName1 = "datetimecolumn1"; + const string dateTimeColumnName2 = "datetimecolumn2"; + const string decimalColumnName1 = "decimalcolumn"; + const string guidColumnName1 = "guidcolumn1"; + const string booleanColumnName1 = "booleancolumn1"; + const string int32ColumnName1 = "int32column1"; + const string int64ColumnName1 = "int64column1"; + const string int64ColumnName2 = "int64column2"; + const string stringColumnName1 = "stringcolumn1"; + const string binaryColumnName1 = "binarycolumn1"; + const string doubleColumnName1 = "doublecolumn1"; + + // Should be extended by remaining types + Provider.AddTable(testTableName, + new Column(dateTimeColumnName1, DbType.DateTime, dateTimeDefaultValue), + new Column(dateTimeColumnName2, DbType.DateTime2, dateTimeDefaultValue), + new Column(decimalColumnName1, DbType.Decimal, decimalDefaultValue), + new Column(guidColumnName1, DbType.Guid, guidDefaultValue), + + // other boolean default values are tested in another test + new Column(booleanColumnName1, DbType.Boolean, true), + + new Column(int32ColumnName1, DbType.Int32, defaultValue: 43), + new Column(int64ColumnName1, DbType.Int64, defaultValue: 88), + new Column(int64ColumnName2, DbType.Int64, defaultValue: 0), + new Column(stringColumnName1, DbType.String, defaultValue: "Hello"), + new Column(binaryColumnName1, DbType.Binary, defaultValue: new byte[] { 12, 32, 34 }), + new Column(doubleColumnName1, DbType.Double, defaultValue: 84.874596567) { Precision = 19, Scale = 10 } + ); + + // Act + var columns = Provider.GetColumns(testTableName); + + // Assert + var dateTimeColumn1 = columns.Single(x => x.Name.Equals(dateTimeColumnName1, StringComparison.OrdinalIgnoreCase)); + var dateTimeColumn2 = columns.Single(x => x.Name.Equals(dateTimeColumnName2, StringComparison.OrdinalIgnoreCase)); + var decimalColumn1 = columns.Single(x => x.Name.Equals(decimalColumnName1, StringComparison.OrdinalIgnoreCase)); + var guidColumn1 = columns.Single(x => x.Name.Equals(guidColumnName1, StringComparison.OrdinalIgnoreCase)); + var booleanColumn1 = columns.Single(x => x.Name.Equals(booleanColumnName1, StringComparison.OrdinalIgnoreCase)); + var int32Column1 = columns.Single(x => x.Name.Equals(int32ColumnName1, StringComparison.OrdinalIgnoreCase)); + var int64Column1 = columns.Single(x => x.Name.Equals(int64ColumnName1, StringComparison.OrdinalIgnoreCase)); + var int64Column2 = columns.Single(x => x.Name.Equals(int64ColumnName2, StringComparison.OrdinalIgnoreCase)); + var stringColumn1 = columns.Single(x => x.Name.Equals(stringColumnName1, StringComparison.OrdinalIgnoreCase)); + var binarycolumn1 = columns.Single(x => x.Name.Equals(binaryColumnName1, StringComparison.OrdinalIgnoreCase)); + var doubleColumn1 = columns.Single(x => x.Name.Equals(doubleColumnName1, StringComparison.OrdinalIgnoreCase)); + + Assert.That(dateTimeColumn1.DefaultValue, Is.EqualTo(dateTimeDefaultValue)); + Assert.That(dateTimeColumn2.DefaultValue, Is.EqualTo(dateTimeDefaultValue)); + Assert.That(decimalColumn1.DefaultValue, Is.EqualTo(decimalDefaultValue)); + Assert.That(guidColumn1.DefaultValue, Is.EqualTo(guidDefaultValue)); + Assert.That(booleanColumn1.DefaultValue, Is.True); + Assert.That(int32Column1.DefaultValue, Is.EqualTo(43)); + Assert.That(int64Column1.DefaultValue, Is.EqualTo(88)); + Assert.That(stringColumn1.DefaultValue, Is.EqualTo("Hello")); + Assert.That(binarycolumn1.DefaultValue, Is.EqualTo(new byte[] { 12, 32, 34 })); + Assert.That(doubleColumn1.DefaultValue, Is.EqualTo(84.874596567)); + } } diff --git a/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_GetColumns_DefaultValueTests.cs b/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_GetColumns_DefaultValueTests.cs index 4cc54d9d..1fe66431 100644 --- a/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_GetColumns_DefaultValueTests.cs +++ b/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_GetColumns_DefaultValueTests.cs @@ -11,7 +11,7 @@ namespace Migrator.Tests.Providers.PostgreSQL; [TestFixture] [Category("Postgre")] -public class PostgreSQLTransformationProvider_GetColumns_DefaultTypeTests : TransformationProvider_GetColumns_GenericTests +public class PostgreSQLTransformationProvider_GetColumns_DefaultValuesTests : TransformationProviderBase { [SetUp] public async Task SetUpAsync() @@ -51,6 +51,74 @@ public void GetColumns_Postgres_DefaultValues_Succeeds() Assert.That(binarycolumn1.DefaultValue, Is.EqualTo(new byte[] { 12, 32, 34 })); } + [Test] + public void GetColumns_DefaultValues_Succeeds() + { + // Arrange + var dateTimeDefaultValue = new DateTime(2000, 1, 2, 3, 4, 5, DateTimeKind.Utc); + var guidDefaultValue = Guid.NewGuid(); + var decimalDefaultValue = 14.56565m; + + const string testTableName = "MyDefaultTestTable"; + + const string dateTimeColumnName1 = "datetimecolumn1"; + const string dateTimeColumnName2 = "datetimecolumn2"; + const string decimalColumnName1 = "decimalcolumn"; + const string guidColumnName1 = "guidcolumn1"; + const string booleanColumnName1 = "booleancolumn1"; + const string int32ColumnName1 = "int32column1"; + const string int64ColumnName1 = "int64column1"; + const string int64ColumnName2 = "int64column2"; + const string stringColumnName1 = "stringcolumn1"; + const string binaryColumnName1 = "binarycolumn1"; + const string doubleColumnName1 = "doublecolumn1"; + + // Should be extended by remaining types + Provider.AddTable(testTableName, + new Column(dateTimeColumnName1, DbType.DateTime, dateTimeDefaultValue), + new Column(dateTimeColumnName2, DbType.DateTime2, dateTimeDefaultValue), + new Column(decimalColumnName1, DbType.Decimal, decimalDefaultValue), + new Column(guidColumnName1, DbType.Guid, guidDefaultValue), + + // other boolean default values are tested in another test + new Column(booleanColumnName1, DbType.Boolean, true), + + new Column(int32ColumnName1, DbType.Int32, defaultValue: 43), + new Column(int64ColumnName1, DbType.Int64, defaultValue: 88), + new Column(int64ColumnName2, DbType.Int64, defaultValue: 0), + new Column(stringColumnName1, DbType.String, defaultValue: "Hello"), + new Column(binaryColumnName1, DbType.Binary, defaultValue: new byte[] { 12, 32, 34 }), + new Column(doubleColumnName1, DbType.Double, defaultValue: 84.874596567) { Precision = 19, Scale = 10 } + ); + + // Act + var columns = Provider.GetColumns(testTableName); + + // Assert + var dateTimeColumn1 = columns.Single(x => x.Name.Equals(dateTimeColumnName1, StringComparison.OrdinalIgnoreCase)); + var dateTimeColumn2 = columns.Single(x => x.Name.Equals(dateTimeColumnName2, StringComparison.OrdinalIgnoreCase)); + var decimalColumn1 = columns.Single(x => x.Name.Equals(decimalColumnName1, StringComparison.OrdinalIgnoreCase)); + var guidColumn1 = columns.Single(x => x.Name.Equals(guidColumnName1, StringComparison.OrdinalIgnoreCase)); + var booleanColumn1 = columns.Single(x => x.Name.Equals(booleanColumnName1, StringComparison.OrdinalIgnoreCase)); + var int32Column1 = columns.Single(x => x.Name.Equals(int32ColumnName1, StringComparison.OrdinalIgnoreCase)); + var int64Column1 = columns.Single(x => x.Name.Equals(int64ColumnName1, StringComparison.OrdinalIgnoreCase)); + var int64Column2 = columns.Single(x => x.Name.Equals(int64ColumnName2, StringComparison.OrdinalIgnoreCase)); + var stringColumn1 = columns.Single(x => x.Name.Equals(stringColumnName1, StringComparison.OrdinalIgnoreCase)); + var binarycolumn1 = columns.Single(x => x.Name.Equals(binaryColumnName1, StringComparison.OrdinalIgnoreCase)); + var doubleColumn1 = columns.Single(x => x.Name.Equals(doubleColumnName1, StringComparison.OrdinalIgnoreCase)); + + Assert.That(dateTimeColumn1.DefaultValue, Is.EqualTo(dateTimeDefaultValue)); + Assert.That(dateTimeColumn2.DefaultValue, Is.EqualTo(dateTimeDefaultValue)); + Assert.That(decimalColumn1.DefaultValue, Is.EqualTo(decimalDefaultValue)); + Assert.That(guidColumn1.DefaultValue, Is.EqualTo(guidDefaultValue)); + Assert.That(booleanColumn1.DefaultValue, Is.True); + Assert.That(int32Column1.DefaultValue, Is.EqualTo(43)); + Assert.That(int64Column1.DefaultValue, Is.EqualTo(88)); + Assert.That(stringColumn1.DefaultValue, Is.EqualTo("Hello")); + Assert.That(binarycolumn1.DefaultValue, Is.EqualTo(new byte[] { 12, 32, 34 })); + Assert.That(doubleColumn1.DefaultValue, Is.EqualTo(84.874596567)); + } + // 1 will coerce to true on inserts but not for default values in Postgre SQL - same for 0 to false // so we do not test it here [TestCase("true", true)] diff --git a/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_GetColumns_Tests.cs b/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_GetColumns_Tests.cs index 5fa61ec0..92f8cb02 100644 --- a/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_GetColumns_Tests.cs +++ b/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_GetColumns_Tests.cs @@ -1,12 +1,12 @@ using System.Threading.Tasks; -using Migrator.Tests.Providers.Generic; +using Migrator.Tests.Providers.Base; using NUnit.Framework; namespace Migrator.Tests.Providers.PostgreSQL; [TestFixture] [Category("Postgre")] -public class PostgreSQLTransformationProvider_GetColumns_Tests : TransformationProvider_GetColumns_GenericTests +public class PostgreSQLTransformationProvider_GetColumns_Tests : TransformationProviderBase { [SetUp] public async Task SetUpAsync() diff --git a/src/Migrator.Tests/Providers/SQLServer/SQLServerTransformationProvider_GetColumnsTests.cs b/src/Migrator.Tests/Providers/SQLServer/SQLServerTransformationProvider_GetColumnsTests.cs index 77caea42..191a7603 100644 --- a/src/Migrator.Tests/Providers/SQLServer/SQLServerTransformationProvider_GetColumnsTests.cs +++ b/src/Migrator.Tests/Providers/SQLServer/SQLServerTransformationProvider_GetColumnsTests.cs @@ -1,12 +1,12 @@ using System.Threading.Tasks; -using Migrator.Tests.Providers.Generic; +using Migrator.Tests.Providers.Base; using NUnit.Framework; namespace Migrator.Tests.Providers.SQLServer; [TestFixture] [Category("SqlServer")] -public class SQLServerTransformationProvider_GetColumnsTests : TransformationProvider_GetColumns_GenericTests +public class SQLServerTransformationProvider_GetColumnsTests : TransformationProviderBase { [SetUp] public async Task SetUpAsync() diff --git a/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_GetColumnsTests.cs b/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_GetColumnsTests.cs index 7d25dd2d..3e7a048e 100644 --- a/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_GetColumnsTests.cs +++ b/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_GetColumnsTests.cs @@ -2,14 +2,14 @@ using System.Threading.Tasks; using DotNetProjects.Migrator.Framework; using DotNetProjects.Migrator.Providers.Impl.SQLite; -using Migrator.Tests.Providers.Generic; +using Migrator.Tests.Providers.Base; using NUnit.Framework; namespace Migrator.Tests.Providers.SQLite; [TestFixture] [Category("SQLite")] -public class SQLiteTransformationProvider_GetColumnsTests : TransformationProvider_GetColumns_GenericTests +public class SQLiteTransformationProvider_GetColumnsTests : TransformationProviderBase { [SetUp] public async Task SetUpAsync() diff --git a/src/Migrator/Providers/Impl/SqlServer/SqlServerTransformationProvider.cs b/src/Migrator/Providers/Impl/SqlServer/SqlServerTransformationProvider.cs index 35c443ee..e69ebc48 100644 --- a/src/Migrator/Providers/Impl/SqlServer/SqlServerTransformationProvider.cs +++ b/src/Migrator/Providers/Impl/SqlServer/SqlServerTransformationProvider.cs @@ -431,6 +431,10 @@ public override Column[] GetColumns(string table) { column.MigratorDbType = MigratorDbType.Guid; } + else if (dataTypeString == "real") + { + column.MigratorDbType = MigratorDbType.Single; + } else { throw new NotImplementedException($"The data type '{dataTypeString}' is not implemented yet. Please file an issue."); From e40d34e9359792846f5b68e88c25bf9cb32bc2cd Mon Sep 17 00:00:00 2001 From: JaBistDuNarrisch Date: Mon, 18 Aug 2025 10:31:44 +0200 Subject: [PATCH 23/27] Moved AddTable test --- ...mationProviderGenericMiscConstraintBase.cs | 10 ------- ...leTransformationProvider_AddTable_Tests.cs | 30 +++++++++++++++++++ ...SQLTransformationProvider_AddTableTests.cs | 23 ++++++++++++++ ...verTransformationProvider_AddTableTests.cs | 30 +++++++++++++++++++ 4 files changed, 83 insertions(+), 10 deletions(-) create mode 100644 src/Migrator.Tests/Providers/OracleProvider/OracleTransformationProvider_AddTable_Tests.cs create mode 100644 src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_AddTableTests.cs create mode 100644 src/Migrator.Tests/Providers/SQLServer/SQLServerTransformationProvider_AddTableTests.cs diff --git a/src/Migrator.Tests/Providers/Generic/TransformationProviderGenericMiscConstraintBase.cs b/src/Migrator.Tests/Providers/Generic/TransformationProviderGenericMiscConstraintBase.cs index 15a05b50..58c99470 100644 --- a/src/Migrator.Tests/Providers/Generic/TransformationProviderGenericMiscConstraintBase.cs +++ b/src/Migrator.Tests/Providers/Generic/TransformationProviderGenericMiscConstraintBase.cs @@ -127,17 +127,7 @@ public void ConstraintExist() Assert.That(Provider.ConstraintExists("abc", "abc"), Is.False); } - [Test] - public void AddTableWithCompoundPrimaryKey() - { - Provider.AddTable("Test", - new Column("PersonId", DbType.Int32, ColumnProperty.PrimaryKey), - new Column("AddressId", DbType.Int32, ColumnProperty.PrimaryKey) - ); - Assert.That(Provider.TableExists("Test"), Is.True, "Table doesn't exist"); - Assert.That(Provider.PrimaryKeyExists("Test", "PK_Test"), Is.True, "Constraint doesn't exist"); - } [Test] public void AddTableWithCompoundPrimaryKeyShouldKeepNullForOtherProperties() diff --git a/src/Migrator.Tests/Providers/OracleProvider/OracleTransformationProvider_AddTable_Tests.cs b/src/Migrator.Tests/Providers/OracleProvider/OracleTransformationProvider_AddTable_Tests.cs new file mode 100644 index 00000000..aa33b236 --- /dev/null +++ b/src/Migrator.Tests/Providers/OracleProvider/OracleTransformationProvider_AddTable_Tests.cs @@ -0,0 +1,30 @@ +using System.Data; +using System.Threading.Tasks; +using DotNetProjects.Migrator.Framework; +using Migrator.Tests.Providers.Base; +using NUnit.Framework; + +namespace Migrator.Tests.Providers.OracleProvider; + +[TestFixture] +[Category("Oracle")] +public class OracleTransformationProvider_AddTable_Tests : TransformationProviderBase +{ + [SetUp] + public async Task SetUpAsync() + { + await BeginOracleTransactionAsync(); + } + + [Test] + public void AddTableWithCompoundPrimaryKey() + { + Provider.AddTable("Test", + new Column("PersonId", DbType.Int32, ColumnProperty.PrimaryKey), + new Column("AddressId", DbType.Int32, ColumnProperty.PrimaryKey) + ); + + Assert.That(Provider.TableExists("Test"), Is.True, "Table doesn't exist"); + Assert.That(Provider.PrimaryKeyExists("Test", "PK_Test"), Is.True, "Constraint doesn't exist"); + } +} \ No newline at end of file diff --git a/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_AddTableTests.cs b/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_AddTableTests.cs new file mode 100644 index 00000000..a1a0059d --- /dev/null +++ b/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_AddTableTests.cs @@ -0,0 +1,23 @@ +using System; +using System.Data; +using DotNetProjects.Migrator.Framework; +using NUnit.Framework; + +namespace Migrator.Tests.Providers.PostgreSQL; + +[TestFixture] +[Category("Postgre")] +public class PostgreSQLTransformationProvider_AddTableTests : PostgreSQLTransformationProviderTestBase +{ + [Test] + public void AddTableWithCompoundPrimaryKey() + { + Provider.AddTable("Test", + new Column("PersonId", DbType.Int32, ColumnProperty.PrimaryKey), + new Column("AddressId", DbType.Int32, ColumnProperty.PrimaryKey) + ); + + Assert.That(Provider.TableExists("Test"), Is.True, "Table doesn't exist"); + Assert.That(Provider.PrimaryKeyExists("Test", "PK_Test"), Is.True, "Constraint doesn't exist"); + } +} diff --git a/src/Migrator.Tests/Providers/SQLServer/SQLServerTransformationProvider_AddTableTests.cs b/src/Migrator.Tests/Providers/SQLServer/SQLServerTransformationProvider_AddTableTests.cs new file mode 100644 index 00000000..3ed92ecc --- /dev/null +++ b/src/Migrator.Tests/Providers/SQLServer/SQLServerTransformationProvider_AddTableTests.cs @@ -0,0 +1,30 @@ +using System.Data; +using System.Threading.Tasks; +using DotNetProjects.Migrator.Framework; +using Migrator.Tests.Providers.Base; +using NUnit.Framework; + +namespace Migrator.Tests.Providers.SQLServer; + +[TestFixture] +[Category("SqlServer")] +public class SQLServerTransformationProvider_AddTableTests : TransformationProviderBase +{ + [SetUp] + public async Task SetUpAsync() + { + await BeginSQLServerTransactionAsync(); + } + + [Test] + public void AddTableWithCompoundPrimaryKey() + { + Provider.AddTable("Test", + new Column("PersonId", DbType.Int32, ColumnProperty.PrimaryKey), + new Column("AddressId", DbType.Int32, ColumnProperty.PrimaryKey) + ); + + Assert.That(Provider.TableExists("Test"), Is.True, "Table doesn't exist"); + Assert.That(Provider.PrimaryKeyExists("Test", "PK_Test"), Is.True, "Constraint doesn't exist"); + } +} From 73a409429c156d0a8927195f2e8c8ea7af1300df Mon Sep 17 00:00:00 2001 From: JaBistDuNarrisch Date: Mon, 18 Aug 2025 12:00:59 +0200 Subject: [PATCH 24/27] Added TableExists and FK exists checks --- .../Base/TransformationProviderSimpleBase.cs | 5 ++++ ...mationProviderGenericMiscConstraintBase.cs | 24 +++++++++++++----- ...TransformationProvider_PrimaryKeyExists.cs | 24 ++++++++++++++++++ ...ostgreSQLTransformationProviderTestBase.cs | 25 +++---------------- ...SQLTransformationProvider_AddTableTests.cs | 1 + ...tionProvider_GetColumnContent_SizeTests.cs | 1 + ...nsformationProvider_GetColumnsTypeTests.cs | 1 + ...formationProvider_PrimaryKeyExistsTests.cs | 20 +++++++++++++++ ...ionProvider_PrimaryKeyWithIdentityTests.cs | 1 + ...TransformationProvider_TableExistsTests.cs | 1 + ...LTransformationProvider_ViewExistsTests.cs | 1 + ...iteTransformationProvider_AddTableTests.cs | 4 ++- .../SQLite/SQLiteTransformationProvider.cs | 15 +++++++++++ .../Providers/TransformationProvider.cs | 16 ++++++++++-- 14 files changed, 109 insertions(+), 30 deletions(-) create mode 100644 src/Migrator.Tests/Providers/OracleProvider/OracleTransformationProvider_PrimaryKeyExists.cs create mode 100644 src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_PrimaryKeyExistsTests.cs diff --git a/src/Migrator.Tests/Providers/Base/TransformationProviderSimpleBase.cs b/src/Migrator.Tests/Providers/Base/TransformationProviderSimpleBase.cs index 82a93a79..40f4725f 100644 --- a/src/Migrator.Tests/Providers/Base/TransformationProviderSimpleBase.cs +++ b/src/Migrator.Tests/Providers/Base/TransformationProviderSimpleBase.cs @@ -38,4 +38,9 @@ public void AddTableWithPrimaryKey() new Column("bigstring", DbType.String, 50000) ); } + + public void AddPrimaryKey() + { + Provider.AddPrimaryKey("PK_Test", "Test", "Id"); + } } diff --git a/src/Migrator.Tests/Providers/Generic/TransformationProviderGenericMiscConstraintBase.cs b/src/Migrator.Tests/Providers/Generic/TransformationProviderGenericMiscConstraintBase.cs index 58c99470..ea6bd79a 100644 --- a/src/Migrator.Tests/Providers/Generic/TransformationProviderGenericMiscConstraintBase.cs +++ b/src/Migrator.Tests/Providers/Generic/TransformationProviderGenericMiscConstraintBase.cs @@ -2,6 +2,8 @@ using System.Data; using System.Linq; using DotNetProjects.Migrator.Framework; +using DotNetProjects.Migrator.Providers.Impl.SQLite; +using DryIoc; using NUnit.Framework; namespace Migrator.Tests.Providers.Generic; @@ -113,10 +115,22 @@ public virtual void RemoveCheckConstraint() [Test] public void RemoveUnexistingForeignKey() { + // Arrange AddForeignKey(); - Provider.RemoveForeignKey("abc", "FK_Test_TestTwo"); - Provider.RemoveForeignKey("abc", "abc"); - Provider.RemoveForeignKey("Test", "abc"); + + // Act/Assert + // Table does not exist. + Assert.Throws(() => Provider.RemoveForeignKey("NotExistingTable", "FK_Test_TestTwo")); + + // Table exists but foreign key does not exist. + if (Provider is SQLiteTransformationProvider) + { + Assert.Throws(() => Provider.RemoveForeignKey("Test", "NotExistingForeignKey")); + } + else + { + Assert.That(() => Provider.RemoveForeignKey("Test", "NotExistingForeignKey"), Throws.Exception); + } } [Test] @@ -124,11 +138,9 @@ public void ConstraintExist() { AddForeignKey(); Assert.That(Provider.ConstraintExists("TestTwo", "FK_Test_TestTwo"), Is.True); - Assert.That(Provider.ConstraintExists("abc", "abc"), Is.False); + Assert.That(Provider.ConstraintExists("TestTwo", "abc"), Is.False); } - - [Test] public void AddTableWithCompoundPrimaryKeyShouldKeepNullForOtherProperties() { diff --git a/src/Migrator.Tests/Providers/OracleProvider/OracleTransformationProvider_PrimaryKeyExists.cs b/src/Migrator.Tests/Providers/OracleProvider/OracleTransformationProvider_PrimaryKeyExists.cs new file mode 100644 index 00000000..54d35731 --- /dev/null +++ b/src/Migrator.Tests/Providers/OracleProvider/OracleTransformationProvider_PrimaryKeyExists.cs @@ -0,0 +1,24 @@ +using System.Threading.Tasks; +using Migrator.Tests.Providers.Base; +using NUnit.Framework; + +namespace Migrator.Tests.Providers.OracleProvider; + +[TestFixture] +[Category("Oracle")] +public class OracleTransformationProvider_PrimaryKeyExistsTests : TransformationProviderSimpleBase +{ + [SetUp] + public async Task SetUpAsync() + { + await BeginOracleTransactionAsync(); + } + + [Test] + public void CanAddPrimaryKey() + { + AddTable(); + AddPrimaryKey(); + Assert.That(Provider.PrimaryKeyExists("Test", "PK_Test"), Is.True); + } +} \ No newline at end of file diff --git a/src/Migrator.Tests/Providers/PostgreSQL/Base/PostgreSQLTransformationProviderTestBase.cs b/src/Migrator.Tests/Providers/PostgreSQL/Base/PostgreSQLTransformationProviderTestBase.cs index d8ce1eb7..9e8de2ac 100644 --- a/src/Migrator.Tests/Providers/PostgreSQL/Base/PostgreSQLTransformationProviderTestBase.cs +++ b/src/Migrator.Tests/Providers/PostgreSQL/Base/PostgreSQLTransformationProviderTestBase.cs @@ -1,33 +1,16 @@ -using DotNetProjects.Migrator.Providers; -using DotNetProjects.Migrator.Providers.Impl.PostgreSQL; +using System.Threading.Tasks; using Migrator.Tests.Providers.Base; -using Migrator.Tests.Settings; -using Migrator.Tests.Settings.Config; using NUnit.Framework; -namespace Migrator.Tests.Providers.PostgreSQL; +namespace Migrator.Tests.Providers.PostgreSQL.Base; [TestFixture] [Category("Postgre")] public abstract class PostgreSQLTransformationProviderTestBase : TransformationProviderSimpleBase { [SetUp] - public void SetUp() + public async Task SetUpAsync() { - var configReader = new ConfigurationReader(); - var connectionString = configReader.GetDatabaseConnectionConfigById(DatabaseConnectionConfigIds.PostgreSQL) - ?.ConnectionString; - - if (string.IsNullOrEmpty(connectionString)) - { - throw new IgnoreException("No Postgre ConnectionString is Set."); - } - - DbProviderFactories.RegisterFactory("Npgsql", () => Npgsql.NpgsqlFactory.Instance); - - Provider = new PostgreSQLTransformationProvider(new PostgreSQLDialect(), connectionString, null, "default", "Npgsql"); - Provider.BeginTransaction(); - - AddDefaultTable(); + await BeginPostgreSQLTransactionAsync(); } } diff --git a/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_AddTableTests.cs b/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_AddTableTests.cs index a1a0059d..7095591f 100644 --- a/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_AddTableTests.cs +++ b/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_AddTableTests.cs @@ -1,6 +1,7 @@ using System; using System.Data; using DotNetProjects.Migrator.Framework; +using Migrator.Tests.Providers.PostgreSQL.Base; using NUnit.Framework; namespace Migrator.Tests.Providers.PostgreSQL; diff --git a/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_GetColumnContent_SizeTests.cs b/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_GetColumnContent_SizeTests.cs index feed0dc9..16c9deda 100644 --- a/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_GetColumnContent_SizeTests.cs +++ b/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_GetColumnContent_SizeTests.cs @@ -1,6 +1,7 @@ using System; using System.Data; using DotNetProjects.Migrator.Framework; +using Migrator.Tests.Providers.PostgreSQL.Base; using NUnit.Framework; namespace Migrator.Tests.Providers.PostgreSQL; diff --git a/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_GetColumnsTypeTests.cs b/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_GetColumnsTypeTests.cs index de7cf454..dd9e1e53 100644 --- a/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_GetColumnsTypeTests.cs +++ b/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_GetColumnsTypeTests.cs @@ -1,6 +1,7 @@ using System.Data; using System.Linq; using DotNetProjects.Migrator.Framework; +using Migrator.Tests.Providers.PostgreSQL.Base; using NUnit.Framework; namespace Migrator.Tests.Providers.PostgreSQL; diff --git a/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_PrimaryKeyExistsTests.cs b/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_PrimaryKeyExistsTests.cs new file mode 100644 index 00000000..67ece8d7 --- /dev/null +++ b/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_PrimaryKeyExistsTests.cs @@ -0,0 +1,20 @@ +using System.Data; +using System.Linq; +using DotNetProjects.Migrator.Framework; +using Migrator.Tests.Providers.PostgreSQL.Base; +using NUnit.Framework; + +namespace Migrator.Tests.Providers.PostgreSQL; + +[TestFixture] +[Category("Postgre")] +public class PostgreSQLTransformationProvider_PrimaryKeyExistsTests : PostgreSQLTransformationProviderTestBase +{ + [Test] + public void CanAddPrimaryKey() + { + AddTable(); + AddPrimaryKey(); + Assert.That(Provider.PrimaryKeyExists("Test", "PK_Test"), Is.True); + } +} diff --git a/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_PrimaryKeyWithIdentityTests.cs b/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_PrimaryKeyWithIdentityTests.cs index 1070bd93..204bad50 100644 --- a/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_PrimaryKeyWithIdentityTests.cs +++ b/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_PrimaryKeyWithIdentityTests.cs @@ -1,6 +1,7 @@ using System; using System.Data; using DotNetProjects.Migrator.Framework; +using Migrator.Tests.Providers.PostgreSQL.Base; using Npgsql; using NUnit.Framework; diff --git a/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_TableExistsTests.cs b/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_TableExistsTests.cs index 38a80a21..fa53a604 100644 --- a/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_TableExistsTests.cs +++ b/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_TableExistsTests.cs @@ -1,5 +1,6 @@ using System.Data; using DotNetProjects.Migrator.Framework; +using Migrator.Tests.Providers.PostgreSQL.Base; using NUnit.Framework; namespace Migrator.Tests.Providers.PostgreSQL; diff --git a/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_ViewExistsTests.cs b/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_ViewExistsTests.cs index 0c57217f..b34dbcc2 100644 --- a/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_ViewExistsTests.cs +++ b/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_ViewExistsTests.cs @@ -1,5 +1,6 @@ using System.Data; using DotNetProjects.Migrator.Framework; +using Migrator.Tests.Providers.PostgreSQL.Base; using NUnit.Framework; namespace Migrator.Tests.Providers.PostgreSQL; diff --git a/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_AddTableTests.cs b/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_AddTableTests.cs index 291d30c0..50fad73a 100644 --- a/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_AddTableTests.cs +++ b/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_AddTableTests.cs @@ -25,7 +25,9 @@ public void AddTable_UniqueOnly_ContainsNull() Assert.That("CREATE TABLE MyTableName (MyColumnName INTEGER NULL UNIQUE)", Is.EqualTo(createScript)); var sqliteInfo = ((SQLiteTransformationProvider)Provider).GetSQLiteTableInfo(tableName); - Assert.That(sqliteInfo.Uniques.Single().KeyColumns.Single(), Is.EqualTo(columnName)); + + // It is no named unique so it is not listed in the Uniques list. Unique on column level is marked as obsolete. + Assert.That(sqliteInfo.Uniques, Is.Empty); } [Test] diff --git a/src/Migrator/Providers/Impl/SQLite/SQLiteTransformationProvider.cs b/src/Migrator/Providers/Impl/SQLite/SQLiteTransformationProvider.cs index 741ada02..492daf0d 100644 --- a/src/Migrator/Providers/Impl/SQLite/SQLiteTransformationProvider.cs +++ b/src/Migrator/Providers/Impl/SQLite/SQLiteTransformationProvider.cs @@ -341,7 +341,17 @@ public DbType ExtractTypeFromColumnDef(string columnDef) public override void RemoveForeignKey(string table, string name) { + if (!TableExists(table)) + { + throw new MigrationException($"Table '{table}' does not exist."); + } + var sqliteTableInfo = GetSQLiteTableInfo(table); + if (!sqliteTableInfo.ForeignKeys.Any(x => x.Name.Equals(name, StringComparison.OrdinalIgnoreCase))) + { + throw new MigrationException($"Foreign key '{name}' does not exist."); + } + sqliteTableInfo.ForeignKeys.RemoveAll(x => x.Name.Equals(name, StringComparison.OrdinalIgnoreCase)); RecreateTable(sqliteTableInfo); @@ -642,6 +652,11 @@ public override void AddPrimaryKey(string name, string tableName, params string[ RecreateTable(sqliteTableInfo); } + public override bool PrimaryKeyExists(string table, string name) + { + throw new NotSupportedException($"SQLite does not support named primary keys. You may wonder why there is a name in method '{nameof(AddPrimaryKey)}'. It is because of architectural decisions of the past. It is overridden in {nameof(SQLiteTransformationProvider)}."); + } + public override void AddUniqueConstraint(string name, string table, params string[] columns) { var sqliteTableInfo = GetSQLiteTableInfo(table); diff --git a/src/Migrator/Providers/TransformationProvider.cs b/src/Migrator/Providers/TransformationProvider.cs index d36c8684..d34aba25 100644 --- a/src/Migrator/Providers/TransformationProvider.cs +++ b/src/Migrator/Providers/TransformationProvider.cs @@ -269,15 +269,27 @@ public virtual string[] GetTables() public virtual void RemoveForeignKey(string table, string name) { + if (!TableExists(table)) + { + throw new MigrationException($"Table '{table}' does not exist."); + } + RemoveConstraint(table, name); } public virtual void RemoveConstraint(string table, string name) { - if (TableExists(table) && ConstraintExists(table, name)) + if (!TableExists(table)) { - ExecuteNonQuery(string.Format("ALTER TABLE {0} DROP CONSTRAINT {1}", QuoteTableNameIfRequired(table), QuoteConstraintNameIfRequired(name))); + throw new MigrationException($"Table '{name}' does not exist"); } + + if (!ConstraintExists(table, name)) + { + throw new MigrationException($"Constraint '{name}' does not exist"); + } + + ExecuteNonQuery(string.Format("ALTER TABLE {0} DROP CONSTRAINT {1}", QuoteTableNameIfRequired(table), QuoteConstraintNameIfRequired(name))); } public virtual void RemoveAllConstraints(string table) From e6b8b9d76e3cfea2e3b258632a2eca58197b107b Mon Sep 17 00:00:00 2001 From: JaBistDuNarrisch Date: Mon, 18 Aug 2025 13:57:25 +0200 Subject: [PATCH 25/27] Adjusted tests due to not supported PrimaryKeyExists in SQLite --- ...ansformationProviderGenericMiscConstraintBase.cs | 10 +++++++++- .../Base/SQLiteTransformationProviderTestBase.cs | 13 +------------ ...QLiteTransformationProvider_RemoveColumnTests.cs | 5 +++-- ...QLiteTransformationProvider_RenameColumnTests.cs | 3 ++- src/Migrator/Providers/TransformationProvider.cs | 5 +++-- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/Migrator.Tests/Providers/Generic/TransformationProviderGenericMiscConstraintBase.cs b/src/Migrator.Tests/Providers/Generic/TransformationProviderGenericMiscConstraintBase.cs index ea6bd79a..800a457d 100644 --- a/src/Migrator.Tests/Providers/Generic/TransformationProviderGenericMiscConstraintBase.cs +++ b/src/Migrator.Tests/Providers/Generic/TransformationProviderGenericMiscConstraintBase.cs @@ -44,7 +44,15 @@ public void AddCheckConstraint() public void CanAddPrimaryKey() { AddPrimaryKey(); - Assert.That(Provider.PrimaryKeyExists("Test", "PK_Test"), Is.True); + + if (Provider is SQLiteTransformationProvider) + { + Assert.Throws(() => Provider.PrimaryKeyExists("Test", "PK_Test")); + } + else + { + Assert.That(Provider.PrimaryKeyExists("Test", "PK_Test"), Is.True); + } } [Test] diff --git a/src/Migrator.Tests/Providers/SQLite/Base/SQLiteTransformationProviderTestBase.cs b/src/Migrator.Tests/Providers/SQLite/Base/SQLiteTransformationProviderTestBase.cs index 4e3d5414..995b0c28 100644 --- a/src/Migrator.Tests/Providers/SQLite/Base/SQLiteTransformationProviderTestBase.cs +++ b/src/Migrator.Tests/Providers/SQLite/Base/SQLiteTransformationProviderTestBase.cs @@ -1,8 +1,5 @@ using System.Threading.Tasks; -using DotNetProjects.Migrator.Providers.Impl.SQLite; using Migrator.Tests.Providers.Base; -using Migrator.Tests.Settings; -using Migrator.Tests.Settings.Config; using NUnit.Framework; namespace Migrator.Tests.Providers.SQLite.Base; @@ -14,15 +11,7 @@ public abstract class SQLiteTransformationProviderTestBase : TransformationProvi [SetUp] public async Task SetUpAsync() { - var configReader = new ConfigurationReader(); - var connectionString = configReader.GetDatabaseConnectionConfigById(DatabaseConnectionConfigIds.SQLiteId) - .ConnectionString; - - Provider = new SQLiteTransformationProvider(new SQLiteDialect(), connectionString, "default", null); - Provider.BeginTransaction(); - + await BeginSQLiteTransactionAsync(); AddDefaultTable(); - - await Task.CompletedTask; } } diff --git a/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_RemoveColumnTests.cs b/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_RemoveColumnTests.cs index 302d23fc..5fd05fa4 100644 --- a/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_RemoveColumnTests.cs +++ b/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_RemoveColumnTests.cs @@ -198,8 +198,9 @@ public void RemoveColumn_HavingMultipleSingleUniques_Succeeds() Provider.RemoveColumn(testTableName, propertyName2); var tableInfoAfter = ((SQLiteTransformationProvider)Provider).GetSQLiteTableInfo(testTableName); - Assert.That(tableInfoBefore.Uniques.Count, Is.EqualTo(2)); - Assert.That(tableInfoAfter.Uniques.Count, Is.EqualTo(1)); + // We do not support not named uniques in SQLite any more. + Assert.That(tableInfoBefore.Uniques.Count, Is.EqualTo(0)); + Assert.That(tableInfoAfter.Uniques.Count, Is.EqualTo(0)); } [Test] diff --git a/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_RenameColumnTests.cs b/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_RenameColumnTests.cs index a69617ec..6796ac30 100644 --- a/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_RenameColumnTests.cs +++ b/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_RenameColumnTests.cs @@ -3,13 +3,14 @@ using DotNetProjects.Migrator.Framework; using DotNetProjects.Migrator.Providers.Impl.SQLite; using Migrator.Tests.Providers.Base; +using Migrator.Tests.Providers.SQLite.Base; using NUnit.Framework; namespace Migrator.Tests.Providers.SQLite; [TestFixture] [Category("SQLite")] -public class SQLiteTransformationProvider_RenameColumnTests : TransformationProviderBase +public class SQLiteTransformationProvider_RenameColumnTests : SQLiteTransformationProviderTestBase { [Test] public void RenameColumn_HavingASingleForeignKeyPointingToTheTargetColumn_SingleColumnForeignKeyIsRemoved() diff --git a/src/Migrator/Providers/TransformationProvider.cs b/src/Migrator/Providers/TransformationProvider.cs index d34aba25..31da115e 100644 --- a/src/Migrator/Providers/TransformationProvider.cs +++ b/src/Migrator/Providers/TransformationProvider.cs @@ -14,6 +14,7 @@ using DotNetProjects.Migrator.Framework; using DotNetProjects.Migrator.Framework.Loggers; using DotNetProjects.Migrator.Framework.SchemaBuilder; +using DotNetProjects.Migrator.Providers.Impl.SQLite; using DotNetProjects.Migrator.Providers.Models; using System; using System.Collections.Generic; @@ -392,9 +393,9 @@ public virtual void AddView(string name, string tableName, params IViewElement[] /// public virtual void AddTable(string name, params IDbField[] columns) { - if (columns.Any(x => x is CheckConstraint)) + if (this is not SQLiteTransformationProvider && columns.Any(x => x is CheckConstraint)) { - throw new MigrationException($"{nameof(CheckConstraint)}s are currently supported in SQLite only."); + throw new MigrationException($"{nameof(CheckConstraint)}s are currently only supported in SQLite."); } // Most databases don't have the concept of a storage engine, so default is to not use it. From 99a623f2f764bd3af14188a0bb27a60c1d10b4e2 Mon Sep 17 00:00:00 2001 From: JaBistDuNarrisch Date: Mon, 18 Aug 2025 15:54:48 +0200 Subject: [PATCH 26/27] override some AddColumn methods in SQLite --- ...mationProviderGenericMiscConstraintBase.cs | 11 +-- .../SQLite/SQLiteTransformationProvider.cs | 78 ++++++++++++++++++- .../Providers/TransformationProvider.cs | 7 +- 3 files changed, 88 insertions(+), 8 deletions(-) diff --git a/src/Migrator.Tests/Providers/Generic/TransformationProviderGenericMiscConstraintBase.cs b/src/Migrator.Tests/Providers/Generic/TransformationProviderGenericMiscConstraintBase.cs index 800a457d..b18acbea 100644 --- a/src/Migrator.Tests/Providers/Generic/TransformationProviderGenericMiscConstraintBase.cs +++ b/src/Migrator.Tests/Providers/Generic/TransformationProviderGenericMiscConstraintBase.cs @@ -3,7 +3,6 @@ using System.Linq; using DotNetProjects.Migrator.Framework; using DotNetProjects.Migrator.Providers.Impl.SQLite; -using DryIoc; using NUnit.Framework; namespace Migrator.Tests.Providers.Generic; @@ -35,7 +34,7 @@ public void AddMultipleUniqueConstraint() Provider.AddUniqueConstraint("UN_Test_TestTwo", "TestTwo", "Id", "TestId"); } - public void AddCheckConstraint() + public void AddTestCheckConstraint() { Provider.AddCheckConstraint("CK_TestTwo_TestId", "TestTwo", "TestId>5"); } @@ -91,8 +90,10 @@ public virtual void CanAddMultipleUniqueConstraint() [Test] public virtual void CanAddCheckConstraint() { - AddCheckConstraint(); - Assert.That(Provider.ConstraintExists("TestTwo", "CK_TestTwo_TestId"), Is.True); + AddTestCheckConstraint(); + var constraintExists = Provider.ConstraintExists("TestTwo", "CK_TestTwo_TestId"); + + Assert.That(constraintExists, Is.True); } [Test] @@ -115,7 +116,7 @@ public void RemoveUniqueConstraint() [Test] public virtual void RemoveCheckConstraint() { - AddCheckConstraint(); + AddTestCheckConstraint(); Provider.RemoveConstraint("TestTwo", "CK_TestTwo_TestId"); Assert.That(Provider.ConstraintExists("TestTwo", "CK_TestTwo_TestId"), Is.False); } diff --git a/src/Migrator/Providers/Impl/SQLite/SQLiteTransformationProvider.cs b/src/Migrator/Providers/Impl/SQLite/SQLiteTransformationProvider.cs index 492daf0d..35279ca9 100644 --- a/src/Migrator/Providers/Impl/SQLite/SQLiteTransformationProvider.cs +++ b/src/Migrator/Providers/Impl/SQLite/SQLiteTransformationProvider.cs @@ -751,6 +751,7 @@ public void RecreateTable(SQLiteTableInfo sqliteTableInfo) var dbFields = columnDbFields.Concat(foreignKeyDbFields) .Concat(uniqueDbFields) + .Concat(checkConstraintDbFields) .ToArray(); // ToHashSet() not available in older .NET versions so we create it old-fashioned. @@ -811,6 +812,12 @@ public void RecreateTable(SQLiteTableInfo sqliteTableInfo) } } + [Obsolete] + public override void AddTable(string table, string engine, string columns) + { + throw new NotSupportedException(); + } + public override void AddColumn(string table, Column column) { if (!TableExists(table)) @@ -819,6 +826,7 @@ public override void AddColumn(string table, Column column) } var sqliteInfo = GetSQLiteTableInfo(table); + if (sqliteInfo.ColumnMappings.Select(x => x.OldName).ToList().Contains(column.Name)) { throw new Exception("Column already exists."); @@ -830,6 +838,61 @@ public override void AddColumn(string table, Column column) RecreateTable(sqliteInfo); } + public override void AddColumn(string table, string columnName, DbType type, ColumnProperty property) + { + var column = new Column(columnName, type, property); + + AddColumn(table, column); + } + + public override void AddColumn(string table, string columnName, MigratorDbType type, ColumnProperty property) + { + var column = new Column(columnName, type, property); + + AddColumn(table, column); + } + + public override void AddColumn(string table, string columnName, DbType type) + { + var column = new Column(columnName, type); + + AddColumn(table, column); + } + + public override void AddColumn(string table, string columnName, MigratorDbType type) + { + var column = new Column(columnName, type); + + AddColumn(table, column); + } + + public override void AddColumn(string table, string columnName, DbType type, int size, ColumnProperty property) + { + var column = new Column(columnName, type, size, property); + + AddColumn(table, column); + } + + public override void AddColumn(string table, string columnName, MigratorDbType type, int size, ColumnProperty property) + { + var column = new Column(columnName, type, size, property); + + AddColumn(table, column); + } + + public override void AddColumn(string table, string columnName, DbType type, object defaultValue) + { + var column = new Column(columnName, type, defaultValue); + + AddColumn(table, column); + } + + public override void AddColumn(string table, string sqlColumn) + { + var column = new Column(sqlColumn); + AddColumn(table, column); + } + public override void ChangeColumn(string table, Column column) { if (!TableExists(table)) @@ -910,9 +973,12 @@ public override string[] GetConstraints(string table) .Select(x => x.Name) .ToList(); - // TODO add PK and CHECK + var checkConstraints = sqliteInfo.CheckConstraints + .Select(x => x.Name) + .ToList(); var names = foreignKeyNames.Concat(uniqueConstraints) + .Concat(checkConstraints) .Where(x => !string.IsNullOrWhiteSpace(x)) .ToArray(); @@ -1471,6 +1537,16 @@ public List GetPragmaTableInfoItems(string tableNameNotQuot return pragmaTableInfoItems; } + public override void AddCheckConstraint(string constraintName, string tableName, string checkSql) + { + var sqliteTableInfo = GetSQLiteTableInfo(tableName); + + var checkConstraint = new CheckConstraint(constraintName, checkSql); + sqliteTableInfo.CheckConstraints.Add(checkConstraint); + + RecreateTable(sqliteTableInfo); + } + public List GetCheckConstraints(string tableName) { if (!TableExists(tableName)) diff --git a/src/Migrator/Providers/TransformationProvider.cs b/src/Migrator/Providers/TransformationProvider.cs index 31da115e..dc511a21 100644 --- a/src/Migrator/Providers/TransformationProvider.cs +++ b/src/Migrator/Providers/TransformationProvider.cs @@ -425,6 +425,7 @@ public virtual void AddTable(string name, string engine, params IDbField[] field var compoundPrimaryKey = pks.Count > 1; var columnProviders = new List(columns.Count()); + foreach (var column in columns) { // Remove the primary key notation if compound primary key because we'll add it back later @@ -447,15 +448,17 @@ public virtual void AddTable(string name, string engine, params IDbField[] field } var indexes = fields.Where(x => x is Index).Cast().ToArray(); + foreach (var index in indexes) { AddIndex(name, index); } var foreignKeys = fields.Where(x => x is ForeignKeyConstraint).Cast().ToArray(); + foreach (var foreignKey in foreignKeys) { - this.AddForeignKey(name, foreignKey); + AddForeignKey(name, foreignKey); } } @@ -647,7 +650,7 @@ public virtual void AddColumn(string table, string column, MigratorDbType type, /// AddColumn(string, string, Type, int, ColumnProperty, object) /// /// - public void AddColumn(string table, string column, DbType type) + public virtual void AddColumn(string table, string column, DbType type) { AddColumn(table, column, type, 0, ColumnProperty.Null, null); } From a126f3397bcbc8eaf65310be319adc30c6f60d01 Mon Sep 17 00:00:00 2001 From: JaBistDuNarrisch Date: Mon, 18 Aug 2025 16:13:12 +0200 Subject: [PATCH 27/27] Added some more overrides in SQLite --- ...mationProviderGenericMiscConstraintBase.cs | 10 ++++----- src/Migrator/Framework/ColumnProperty.cs | 1 + .../SQLite/SQLiteTransformationProvider.cs | 22 +++++++++++++++++++ .../Providers/TransformationProvider.cs | 2 +- 4 files changed, 29 insertions(+), 6 deletions(-) diff --git a/src/Migrator.Tests/Providers/Generic/TransformationProviderGenericMiscConstraintBase.cs b/src/Migrator.Tests/Providers/Generic/TransformationProviderGenericMiscConstraintBase.cs index b18acbea..84ca3c1d 100644 --- a/src/Migrator.Tests/Providers/Generic/TransformationProviderGenericMiscConstraintBase.cs +++ b/src/Migrator.Tests/Providers/Generic/TransformationProviderGenericMiscConstraintBase.cs @@ -54,11 +54,11 @@ public void CanAddPrimaryKey() } } - [Test] - public void AddIndexedColumn() - { - Provider.AddColumn("TestTwo", "Test", DbType.String, 50, ColumnProperty.Indexed); - } + // [Test] + // public void AddIndexedColumn() + // { + // Provider.AddColumn("TestTwo", "Test", DbType.String, 50, ColumnProperty.Indexed); + // } [Test] public void AddUniqueColumn() diff --git a/src/Migrator/Framework/ColumnProperty.cs b/src/Migrator/Framework/ColumnProperty.cs index a49ddd1b..c89297e0 100644 --- a/src/Migrator/Framework/ColumnProperty.cs +++ b/src/Migrator/Framework/ColumnProperty.cs @@ -34,6 +34,7 @@ public enum ColumnProperty /// /// Indexed Column /// + [Obsolete("Use method 'AddIndex'")] Indexed = 1 << 4, /// diff --git a/src/Migrator/Providers/Impl/SQLite/SQLiteTransformationProvider.cs b/src/Migrator/Providers/Impl/SQLite/SQLiteTransformationProvider.cs index 35279ca9..fdb3a0be 100644 --- a/src/Migrator/Providers/Impl/SQLite/SQLiteTransformationProvider.cs +++ b/src/Migrator/Providers/Impl/SQLite/SQLiteTransformationProvider.cs @@ -838,6 +838,20 @@ public override void AddColumn(string table, Column column) RecreateTable(sqliteInfo); } + public override void AddColumn(string table, string columnName, DbType type, int size) + { + var column = new Column(columnName, type, size); + + AddColumn(table, column); + } + + public override void AddColumn(string table, string columnName, MigratorDbType type, int size) + { + var column = new Column(columnName, type, size); + + AddColumn(table, column); + } + public override void AddColumn(string table, string columnName, DbType type, ColumnProperty property) { var column = new Column(columnName, type, property); @@ -852,6 +866,14 @@ public override void AddColumn(string table, string columnName, MigratorDbType t AddColumn(table, column); } + public override void AddColumn(string table, string columnName, MigratorDbType type, int size, ColumnProperty property, + object defaultValue) + { + var column = new Column(columnName, type, property) { Size = size, DefaultValue = defaultValue }; + + AddColumn(table, column); + } + public override void AddColumn(string table, string columnName, DbType type) { var column = new Column(columnName, type); diff --git a/src/Migrator/Providers/TransformationProvider.cs b/src/Migrator/Providers/TransformationProvider.cs index dc511a21..94747d5d 100644 --- a/src/Migrator/Providers/TransformationProvider.cs +++ b/src/Migrator/Providers/TransformationProvider.cs @@ -670,7 +670,7 @@ public virtual void AddColumn(string table, string column, MigratorDbType type) /// AddColumn(string, string, Type, int, ColumnProperty, object) /// /// - public void AddColumn(string table, string column, DbType type, int size) + public virtual void AddColumn(string table, string column, DbType type, int size) { AddColumn(table, column, type, size, ColumnProperty.Null, null); }