diff --git a/src/Migrator.Tests/Providers/Base/TransformationProviderBase.cs b/src/Migrator.Tests/Providers/Base/TransformationProviderBase.cs
index 6ad3c5c1..f16ff560 100644
--- a/src/Migrator.Tests/Providers/Base/TransformationProviderBase.cs
+++ b/src/Migrator.Tests/Providers/Base/TransformationProviderBase.cs
@@ -1,468 +1,154 @@
using System;
-using System.Data;
+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 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;
+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.
+/// Base class for provider tests.
///
-public abstract class TransformationProviderBase : TransformationProviderSimpleBase
+public abstract class TransformationProviderBase
{
- [Test]
- public void TableExistsWorks()
+ [TearDown]
+ public virtual void TearDown()
{
- 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);
- }
+ DropTestTables();
- [Test]
- public void CanExecuteBadSqlForNonCurrentProvider()
- {
- Provider["foo"].ExecuteNonQuery("select foo from bar 123");
- }
-
- [Test]
- public void TableCanBeAdded()
- {
- AddTable();
- Assert.That(Provider.TableExists("Test"), Is.True);
+ Provider?.Rollback();
}
- [Test]
- public void GetTablesWorks()
+ protected void DropTestTables()
{
- foreach (var name in Provider.GetTables())
+ // Because MySql doesn't support schema transaction
+ // we got to remove the tables manually... sad...
+ try
{
- Provider.Logger.Log("Table: {0}", name);
+ Provider.RemoveTable("TestTwo");
}
-
- 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)
+ catch (Exception)
{
- 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(() =>
+ try
{
- 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.RemoveTable("Test");
+ }
+ catch (Exception)
{
- 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)
+ }
+ try
+ {
+ Provider.RemoveTable("SchemaInfo");
+ }
+ catch (Exception)
{
- 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);
- }
+ protected ITransformationProvider Provider;
- [Test]
- public void CanInsertNullData()
+ protected async Task BeginOracleTransactionAsync()
{
- AddTable();
+ using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10));
+ var configReader = new ConfigurationReader();
- Provider.Insert("Test", ["Id", "Title"], [1, "foo"]);
- Provider.Insert("Test", ["Id", "Title"], [2, null]);
+ var databaseConnectionConfig = configReader.GetDatabaseConnectionConfigById(DatabaseConnectionConfigIds.OracleId);
- using var cmd = Provider.CreateCommand();
- using var reader = Provider.Select(cmd, "Title", "Test");
- var vals = GetStringVals(reader);
+ var connectionString = databaseConnectionConfig?.ConnectionString;
- 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);
- }
+ if (string.IsNullOrEmpty(connectionString))
+ {
+ throw new IgnoreException($"No Oracle {nameof(DatabaseConnectionConfig.ConnectionString)} is set.");
+ }
- [Test]
- public void CanInsertDataWithSingleQuotes()
- {
- // Arrange
- const string testString = "Test string with ' (single quote)";
- AddTable();
- Provider.Insert("Test", ["Id", "Title"], [1, testString]);
+ DbProviderFactories.RegisterFactory("Oracle.ManagedDataAccess.Client", () => Oracle.ManagedDataAccess.Client.OracleClientFactory.Instance);
- using var cmd = Provider.CreateCommand();
- using var reader = Provider.Select(cmd, "Title", "Test");
+ 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);
- Assert.That(reader.Read(), Is.True);
- Assert.That(testString, Is.EqualTo(reader.GetString(0)));
- Assert.That(reader.Read(), Is.False);
- }
+ Provider = new OracleTransformationProvider(new OracleDialect(), databaseInfo.DatabaseConnectionConfig.ConnectionString, null, "default", "Oracle.ManagedDataAccess.Client");
- [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);
+ Provider.BeginTransaction();
}
- [Test]
- public void DeleteDataWithArrays()
+ protected async Task BeginPostgreSQLTransactionAsync()
{
- InsertData();
-
- Provider.Delete("TestTwo", ["TestId"], [1]);
+ using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10));
+ var configReader = new ConfigurationReader();
- using var cmd = Provider.CreateCommand();
- using var reader = Provider.Select(cmd, "TestId", "TestTwo");
+ var databaseConnectionConfig = configReader.GetDatabaseConnectionConfigById(DatabaseConnectionConfigIds.PostgreSQL);
- 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]);
+ var connectionString = databaseConnectionConfig?.ConnectionString;
- Provider.Update("TestTwo", ["TestId"], [3]);
- using var cmd = Provider.CreateCommand();
- using var reader = Provider.Select(cmd, "TestId", "TestTwo");
- var vals = GetVals(reader);
+ if (string.IsNullOrEmpty(connectionString))
+ {
+ throw new IgnoreException("No Postgre SQL connection string is set.");
+ }
- 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);
- }
+ DbProviderFactories.RegisterFactory("Npgsql", () => Npgsql.NpgsqlFactory.Instance);
- [Test]
- public void CanUpdateWithNullData()
- {
- AddTable();
- Provider.Insert("Test", ["Id", "Title"], [1, "foo"]);
- Provider.Insert("Test", ["Id", "Title"], [2, null]);
+ 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.Update("Test", ["Title"], [null]);
- using var cmd = Provider.CreateCommand();
- using var reader = Provider.Select(cmd, "Title", "Test");
- var vals = GetStringVals(reader);
+ Provider = new PostgreSQLTransformationProvider(new PostgreSQLDialect(), databaseInfo.DatabaseConnectionConfig.ConnectionString, null, "default", "Npgsql");
+ Provider.BeginTransaction();
- Assert.That(vals[0], Is.Null);
- Assert.That(vals[1], Is.Null);
+ await Task.CompletedTask;
}
- [Test]
- public void UpdateDataWithWhere()
+ protected async Task BeginSQLiteTransactionAsync()
{
- Provider.Insert("TestTwo", ["Id", "TestId"], [10, 1]);
- Provider.Insert("TestTwo", ["Id", "TestId"], [11, 2]);
+ var configReader = new ConfigurationReader();
+ var connectionString = configReader.GetDatabaseConnectionConfigById(DatabaseConnectionConfigIds.SQLiteId)
+ .ConnectionString;
- Provider.Update("TestTwo", ["TestId"], [3], "TestId='1'");
- using var cmd = Provider.CreateCommand();
- using var reader = Provider.Select(cmd, "TestId", "TestTwo");
- var vals = GetVals(reader);
+ Provider = new SQLiteTransformationProvider(new SQLiteDialect(), connectionString, "default", null);
+ Provider.BeginTransaction();
- 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);
+ await Task.CompletedTask;
}
- [Test]
- public void AddIndex()
+ protected async Task BeginSQLServerTransactionAsync()
{
- 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);
- }
+ var configReader = new ConfigurationReader();
- [Test]
- public void RemoveIndex()
- {
- var indexName = "test_index";
+ var databaseConnectionConfig = configReader.GetDatabaseConnectionConfigById(DatabaseConnectionConfigIds.SQLServerId);
- 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);
- }
+ var connectionString = databaseConnectionConfig?.ConnectionString;
+ if (string.IsNullOrEmpty(connectionString))
+ {
+ throw new IgnoreException($"No SQL Server {nameof(DatabaseConnectionConfig.ConnectionString)} is set.");
+ }
- 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]);
+ DbProviderFactories.RegisterFactory("Microsoft.Data.SqlClient", () => Microsoft.Data.SqlClient.SqlClientFactory.Instance);
- return vals;
- }
+ 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);
- 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 SqlServerTransformationProvider(new SqlServerDialect(), databaseInfo.DatabaseConnectionConfig.ConnectionString, "dbo", "default", "Microsoft.Data.SqlClient");
- return vals;
+ Provider.BeginTransaction();
}
}
diff --git a/src/Migrator.Tests/Providers/Base/TransformationProviderSimpleBase.cs b/src/Migrator.Tests/Providers/Base/TransformationProviderSimpleBase.cs
index 7a599de4..40f4725f 100644
--- a/src/Migrator.Tests/Providers/Base/TransformationProviderSimpleBase.cs
+++ b/src/Migrator.Tests/Providers/Base/TransformationProviderSimpleBase.cs
@@ -5,45 +5,8 @@
namespace Migrator.Tests.Providers.Base;
-public abstract class TransformationProviderSimpleBase
+public abstract class TransformationProviderSimpleBase : TransformationProviderBase
{
- protected ITransformationProvider Provider;
-
- [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",
@@ -75,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/Base/TransformationProviderConstraintBase.cs b/src/Migrator.Tests/Providers/Generic/TransformationProviderGenericMiscConstraintBase.cs
similarity index 78%
rename from src/Migrator.Tests/Providers/Base/TransformationProviderConstraintBase.cs
rename to src/Migrator.Tests/Providers/Generic/TransformationProviderGenericMiscConstraintBase.cs
index 8a096bcd..84ca3c1d 100644
--- a/src/Migrator.Tests/Providers/Base/TransformationProviderConstraintBase.cs
+++ b/src/Migrator.Tests/Providers/Generic/TransformationProviderGenericMiscConstraintBase.cs
@@ -2,14 +2,15 @@
using System.Data;
using System.Linq;
using DotNetProjects.Migrator.Framework;
+using DotNetProjects.Migrator.Providers.Impl.SQLite;
using NUnit.Framework;
-namespace Migrator.Tests.Providers;
+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 TransformationProviderConstraintBase : TransformationProviderBase
+public abstract class TransformationProviderGenericMiscConstraintBase : TransformationProviderGenericMiscTests
{
public void AddForeignKey()
{
@@ -33,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");
}
@@ -42,15 +43,23 @@ public void AddCheckConstraint()
public void CanAddPrimaryKey()
{
AddPrimaryKey();
- Assert.That(Provider.PrimaryKeyExists("Test", "PK_Test"), Is.True);
- }
- [Test]
- public void AddIndexedColumn()
- {
- Provider.AddColumn("TestTwo", "Test", DbType.String, 50, ColumnProperty.Indexed);
+ if (Provider is SQLiteTransformationProvider)
+ {
+ Assert.Throws(() => Provider.PrimaryKeyExists("Test", "PK_Test"));
+ }
+ else
+ {
+ Assert.That(Provider.PrimaryKeyExists("Test", "PK_Test"), Is.True);
+ }
}
+ // [Test]
+ // public void AddIndexedColumn()
+ // {
+ // Provider.AddColumn("TestTwo", "Test", DbType.String, 50, ColumnProperty.Indexed);
+ // }
+
[Test]
public void AddUniqueColumn()
{
@@ -81,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]
@@ -105,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);
}
@@ -113,10 +124,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,32 +147,21 @@ public void ConstraintExist()
{
AddForeignKey();
Assert.That(Provider.ConstraintExists("TestTwo", "FK_Test_TestTwo"), Is.True);
- 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");
+ Assert.That(Provider.ConstraintExists("TestTwo", "abc"), Is.False);
}
[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/Generic/TransformationProviderGenericMiscTests.cs b/src/Migrator.Tests/Providers/Generic/TransformationProviderGenericMiscTests.cs
new file mode 100644
index 00000000..65971c7e
--- /dev/null
+++ b/src/Migrator.Tests/Providers/Generic/TransformationProviderGenericMiscTests.cs
@@ -0,0 +1,474 @@
+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 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()
+ {
+ var tables = Provider.GetTables();
+
+ foreach (var name in tables)
+ {
+ Provider.Logger.Log("Table: {0}", name);
+ }
+
+ Assert.That(1, Is.EqualTo(tables.Length));
+ AddTable();
+
+ tables = Provider.GetTables();
+
+ Assert.That(2, Is.EqualTo(tables.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..8839dc37 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();
+ await BeginOracleTransactionAsync();
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..d7ffcdce
--- /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 BeginOracleTransactionAsync();
+
+ 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));
- }
-}
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/OracleProvider/OracleTransformationProvider_GetColumns_Tests.cs b/src/Migrator.Tests/Providers/OracleProvider/OracleTransformationProvider_GetColumns_Tests.cs
new file mode 100644
index 00000000..7993fbba
--- /dev/null
+++ b/src/Migrator.Tests/Providers/OracleProvider/OracleTransformationProvider_GetColumns_Tests.cs
@@ -0,0 +1,114 @@
+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.OracleProvider;
+
+[TestFixture]
+[Category("Oracle")]
+public class OracleTransformationProvider_GetColumns_Tests : TransformationProviderBase
+{
+ [SetUp]
+ 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 }));
+ }
+
+ [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_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/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 a1e973ec..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;
-
-[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/PostgreSQL/PostgreSQLTransformationProvider_AddTableTests.cs b/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_AddTableTests.cs
new file mode 100644
index 00000000..7095591f
--- /dev/null
+++ b/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_AddTableTests.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Data;
+using DotNetProjects.Migrator.Framework;
+using Migrator.Tests.Providers.PostgreSQL.Base;
+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/PostgreSQL/PostgreSQLTransformationProvider_GetColumnContentSizeTests.cs b/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_GetColumnContent_SizeTests.cs
similarity index 94%
rename from src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_GetColumnContentSizeTests.cs
rename to src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_GetColumnContent_SizeTests.cs
index d2a3701f..16c9deda 100644
--- a/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_GetColumnContentSizeTests.cs
+++ b/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_GetColumnContent_SizeTests.cs
@@ -1,13 +1,14 @@
using System;
using System.Data;
using DotNetProjects.Migrator.Framework;
+using Migrator.Tests.Providers.PostgreSQL.Base;
using NUnit.Framework;
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_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_GetColumnsDefaultValueTests.cs b/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_GetColumns_DefaultValueTests.cs
similarity index 63%
rename from src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_GetColumnsDefaultValueTests.cs
rename to src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_GetColumns_DefaultValueTests.cs
index 0e9447b6..1fe66431 100644
--- a/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_GetColumnsDefaultValueTests.cs
+++ b/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_GetColumns_DefaultValueTests.cs
@@ -1,16 +1,55 @@
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_GetColumnsDefaultTypeTests : PostgreSQLTransformationProviderTestBase
+public class PostgreSQLTransformationProvider_GetColumns_DefaultValuesTests : TransformationProviderBase
{
- private const decimal DecimalDefaultValue = 14.56565m;
+ [SetUp]
+ public async Task SetUpAsync()
+ {
+ await BeginPostgreSQLTransactionAsync();
+ }
+
+ ///
+ /// More tests for GetColumns in
+ ///
+ [Test]
+ 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(binaryColumnName1, DbType.Binary, defaultValue: new byte[] { 12, 32, 34 })
+ );
+
+ // Act
+ var columns = Provider.GetColumns(testTableName);
+
+ // 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 }));
+ }
[Test]
public void GetColumns_DefaultValues_Succeeds()
@@ -18,6 +57,7 @@ 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";
@@ -32,14 +72,12 @@ public void GetColumns_DefaultValues_Succeeds()
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(decimalColumnName1, DbType.Decimal, decimalDefaultValue),
new Column(guidColumnName1, DbType.Guid, guidDefaultValue),
// other boolean default values are tested in another test
@@ -50,41 +88,35 @@ 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(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(doubleColumnName1, DbType.Double, defaultValue: 84.874596567) { Precision = 19, Scale = 10 }
);
// 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);
- var intervalColumn1 = columns.Single(x => x.Name == intervalColumnName1);
- var intervalColumn2 = columns.Single(x => x.Name == intervalColumnName2);
+ 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(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)));
+ 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
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..92f8cb02
--- /dev/null
+++ b/src/Migrator.Tests/Providers/PostgreSQL/PostgreSQLTransformationProvider_GetColumns_Tests.cs
@@ -0,0 +1,16 @@
+using System.Threading.Tasks;
+using Migrator.Tests.Providers.Base;
+using NUnit.Framework;
+
+namespace Migrator.Tests.Providers.PostgreSQL;
+
+[TestFixture]
+[Category("Postgre")]
+public class PostgreSQLTransformationProvider_GetColumns_Tests : TransformationProviderBase
+{
+ [SetUp]
+ public async Task SetUpAsync()
+ {
+ await BeginPostgreSQLTransactionAsync();
+ }
+}
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/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_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");
+ }
+}
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..191a7603
--- /dev/null
+++ b/src/Migrator.Tests/Providers/SQLServer/SQLServerTransformationProvider_GetColumnsTests.cs
@@ -0,0 +1,16 @@
+using System.Threading.Tasks;
+using Migrator.Tests.Providers.Base;
+using NUnit.Framework;
+
+namespace Migrator.Tests.Providers.SQLServer;
+
+[TestFixture]
+[Category("SqlServer")]
+public class SQLServerTransformationProvider_GetColumnsTests : TransformationProviderBase
+{
+ [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 e574d275..f89967d6 100644
--- a/src/Migrator.Tests/Providers/SQLServer/SqlServerTransformationProviderGenericTests.cs
+++ b/src/Migrator.Tests/Providers/SQLServer/SqlServerTransformationProviderGenericTests.cs
@@ -1,47 +1,20 @@
-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.Settings;
-using Migrator.Tests.Settings.Config;
-using Migrator.Tests.Settings.Models;
+using Migrator.Tests.Providers.Generic;
using NUnit.Framework;
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();
+ await BeginSQLServerTransactionAsync();
AddDefaultTable();
}
diff --git a/src/Migrator.Tests/Providers/SQLite/Base/SQLiteTransformationProviderTestBase.cs b/src/Migrator.Tests/Providers/SQLite/Base/SQLiteTransformationProviderTestBase.cs
index 69cd6a1a..995b0c28 100644
--- a/src/Migrator.Tests/Providers/SQLite/Base/SQLiteTransformationProviderTestBase.cs
+++ b/src/Migrator.Tests/Providers/SQLite/Base/SQLiteTransformationProviderTestBase.cs
@@ -1,7 +1,5 @@
-using DotNetProjects.Migrator.Providers.Impl.SQLite;
+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.SQLite.Base;
@@ -11,15 +9,9 @@ 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)
- .ConnectionString;
-
- Provider = new SQLiteTransformationProvider(new SQLiteDialect(), connectionString, "default", null);
- Provider.BeginTransaction();
-
+ await BeginSQLiteTransactionAsync();
AddDefaultTable();
}
}
diff --git a/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProviderGenericTests.cs b/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProviderGenericTests.cs
index af56f8cd..2a7ba7ba 100644
--- a/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProviderGenericTests.cs
+++ b/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProviderGenericTests.cs
@@ -1,23 +1,17 @@
-using DotNetProjects.Migrator.Providers.Impl.SQLite;
-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.Base;
+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_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.Tests/Providers/SQLite/SQLiteTransformationProvider_GetCheckConstraintsTests.cs b/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_GetCheckConstraintsTests.cs
new file mode 100644
index 00000000..c9e20eca
--- /dev/null
+++ b/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_GetCheckConstraintsTests.cs
@@ -0,0 +1,44 @@
+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))"));
+ }
+}
diff --git a/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_GetColumnsTests.cs b/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_GetColumnsTests.cs
index da14ce25..3e7a048e 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.Base;
using NUnit.Framework;
namespace Migrator.Tests.Providers.SQLite;
[TestFixture]
[Category("SQLite")]
-public class SQLiteTransformationProvider_GetColumnsTests : SQLiteTransformationProviderTestBase
+public class SQLiteTransformationProvider_GetColumnsTests : TransformationProviderBase
{
+ [SetUp]
+ public async Task SetUpAsync()
+ {
+ await BeginSQLiteTransactionAsync();
+ }
+
[Test]
public void GetColumns_UniqueButNotPrimaryKey_ReturnsFalse()
{
@@ -94,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
diff --git a/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_RemoveColumnTests.cs b/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_RemoveColumnTests.cs
index 9c089e1c..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]
@@ -265,4 +266,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.Tests/Providers/SQLite/SQLiteTransformationProvider_RenameColumnTests.cs b/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_RenameColumnTests.cs
index 8821031d..6796ac30 100644
--- a/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_RenameColumnTests.cs
+++ b/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_RenameColumnTests.cs
@@ -1,8 +1,8 @@
-using System;
using System.Data;
using System.Linq;
using DotNetProjects.Migrator.Framework;
using DotNetProjects.Migrator.Providers.Impl.SQLite;
+using Migrator.Tests.Providers.Base;
using Migrator.Tests.Providers.SQLite.Base;
using NUnit.Framework;
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/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/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
diff --git a/src/Migrator/Providers/Dialect.cs b/src/Migrator/Providers/Dialect.cs
index ac15af49..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)
@@ -366,7 +371,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/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 cb51faf3..7c3a20ad 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;
@@ -16,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)");
@@ -37,6 +40,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)");
@@ -118,17 +122,57 @@ public override ColumnPropertiesMapper GetColumnMapper(Column column)
public override string Default(object defaultValue)
{
- if (defaultValue.GetType().Equals(typeof(bool)))
+ if (defaultValue == null)
+ {
+ return string.Empty;
+ }
+
+ if (defaultValue is bool booleanValue)
+ {
+ return string.Format("DEFAULT {0}", booleanValue ? "1" : "0");
+ }
+ else if (defaultValue is Guid guid)
+ {
+ 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)
{
- return string.Format("DEFAULT {0}", (bool)defaultValue ? "1" : "0");
+ // 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')";
}
- else if (defaultValue is Guid)
+ else if (defaultValue is string stringValue)
{
- return string.Format("DEFAULT HEXTORAW('{0}')", defaultValue.ToString().Replace("-", ""));
+ stringValue = stringValue.Replace("'", "''");
+ return $"DEFAULT '{stringValue}'";
}
- else if (defaultValue is DateTime)
+ 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..337250d1 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,114 +387,287 @@ 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();
+ stringBuilder.AppendLine("SELECT");
+ stringBuilder.AppendLine(" COLUMN_NAME,");
+ 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");
+ 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())
- 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 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");
+
+ 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);
+ 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 (dataType.Equals("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 = Convert.ToInt32(reader.GetValue(3));
- var scale = Convert.ToInt32(reader.GetValue(4));
- if (scale == 0)
+ column.Precision = dataPrecision;
+
+ if (dataScale > 0)
{
- colType = precision <= 10 ? DbType.Int16 : DbType.Int64;
+ // Could also be Double
+ column.MigratorDbType = MigratorDbType.Decimal;
+ column.Scale = dataScale;
}
else
{
- colType = DbType.Decimal;
+ if (dataPrecision.HasValue && dataPrecision == 1)
+ {
+ column.MigratorDbType = MigratorDbType.Boolean;
+ }
+ else if (dataPrecision.HasValue && (dataPrecision == 0 || (2 <= dataPrecision && dataPrecision <= 5)))
+ {
+ column.MigratorDbType = MigratorDbType.Int16;
+ }
+ else if (dataPrecision.HasValue && 6 <= dataPrecision && dataPrecision <= 10)
+ {
+ column.MigratorDbType = MigratorDbType.Int32;
+ }
+ 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();
+ }
}
}
- else if (dataType.StartsWith("timestamp") || dataType.Equals("date"))
+ else if (dataTypeString.StartsWith("TIMESTAMP"))
{
- colType = DbType.DateTime;
- }
-
- var columnProperties = (isNullable) ? ColumnProperty.Null : ColumnProperty.NotNull;
- var column = new Column(colName, colType, columnProperties);
+ var timestampNumberRegex = new Regex(@"(?<=^Timestamp\()[\d]+(?=\)$)", RegexOptions.IgnoreCase);
+ var timestampNumberMatch = timestampNumberRegex.Match(dataTypeString);
- if (defaultValue != null && defaultValue != DBNull.Value)
+ 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.DefaultValue = defaultValue;
+ column.MigratorDbType = MigratorDbType.Date;
}
-
- if (column.DefaultValue is string && ((string)column.DefaultValue).StartsWith("'") && ((string)column.DefaultValue).EndsWith("'"))
+ else if (dataTypeString == "RAW" && dataLength == 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 if (dataTypeString == "BOOLEAN")
+ {
+ column.MigratorDbType = MigratorDbType.Boolean;
+ }
+ else if (dataTypeString == "NCLOB")
{
- column.DefaultValue = ((string)column.DefaultValue).Substring(1, ((string)column.DefaultValue).Length - 2);
+ column.MigratorDbType = MigratorDbType.String;
+ }
+ else
+ {
+ throw new NotImplementedException();
}
- if ((column.DefaultValue is string s && !string.IsNullOrEmpty(s)) ||
- column.DefaultValue is not string && column.DefaultValue != null)
+ if (!string.IsNullOrWhiteSpace(dataDefaultString))
{
if (column.Type == DbType.Int16 || column.Type == DbType.Int32 || column.Type == DbType.Int64)
{
- column.DefaultValue = long.Parse(column.DefaultValue.ToString());
+ column.DefaultValue = long.Parse(dataDefaultString, CultureInfo.InvariantCulture);
}
- else if (column.Type == DbType.UInt16 || column.Type == DbType.UInt32 || column.Type == DbType.UInt64)
+ else if (column.Type == DbType.Double)
{
- column.DefaultValue = ulong.Parse(column.DefaultValue.ToString());
+ column.DefaultValue = double.Parse(dataDefaultString, CultureInfo.InvariantCulture);
}
- else if (column.Type == DbType.Double || column.Type == DbType.Single)
+ else if (column.Type == DbType.Single)
{
- column.DefaultValue = double.Parse(column.DefaultValue.ToString());
+ 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 = column.DefaultValue.ToString().Trim() == "1" || column.DefaultValue.ToString().Trim().ToUpper() == "TRUE";
+ column.DefaultValue = dataDefaultString == "1" || dataDefaultString.ToUpper() == "TRUE";
}
else if (column.Type == DbType.DateTime || column.Type == DbType.DateTime2)
{
- 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)
+ if (dataDefaultString.StartsWith("TO_TIMESTAMP("))
{
- var dt = defVal;
- if (defVal.StartsWith("'"))
+ var expectedOracleToTimestampPattern = "YYYY-MM-DD HH24:MI:SS";
+
+ if (!dataDefaultString.Contains(expectedOracleToTimestampPattern))
{
- dt = defVal.Substring(1, defVal.Length - 2);
+ throw new NotSupportedException($"Not supported 'TO_TIMESTAMP' pattern. Expected pattern: {expectedOracleToTimestampPattern}");
}
- var d = DateTime.ParseExact(dt, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture);
- column.DefaultValue = d;
+ 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 (timestampRegex.Match(dataDefaultString) is Match timestampMatch && timestampMatch.Success)
+ {
+ 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
+ {
+ // Could be system time in many variants
+ column.DefaultValue = dataDefaultString;
}
}
else if (column.Type == DbType.Guid)
{
- if (column.DefaultValue is string defValCv && defValCv.StartsWith("HEXTORAW("))
+ if (hexToRawRegex.Match(dataDefaultString) is Match hexToRawMatch && hexToRawMatch.Success)
{
- var dt = defValCv.Substring((defValCv.IndexOf("'") + 1), defValCv.IndexOf("'", defValCv.IndexOf("'") + 1) - defValCv.IndexOf("'") - 1);
- var d = Guid.Parse(dt);
- column.DefaultValue = d;
+ 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
+ Array.Reverse(bytes, 0, 4);
+ Array.Reverse(bytes, 4, 2);
+ Array.Reverse(bytes, 6, 2);
+
+ column.DefaultValue = new Guid(bytes);
}
- else if (column.DefaultValue is string defVal)
+ else if (dataDefaultString.StartsWith("'"))
{
- var dt = defVal;
- if (defVal.StartsWith("'"))
- {
- dt = defVal.Substring(1, defVal.Length - 2);
- }
+ var guidString = dataDefaultString.Substring(1, dataDefaultString.Length - 2);
+
+ column.DefaultValue = Guid.Parse(guidString);
+ }
+ else
+ {
+ column.DefaultValue = dataDefaultString;
+ }
+ }
+ else if (column.Type == DbType.String)
+ {
+ var contentRegex = new Regex(@"(?<=^').*(?='$)");
- var d = Guid.Parse(dt);
- column.DefaultValue = d;
+ 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;
+ }
}
columns.Add(column);
@@ -501,24 +677,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;
diff --git a/src/Migrator/Providers/Impl/PostgreSQL/PostgreSQLDialect.cs b/src/Migrator/Providers/Impl/PostgreSQL/PostgreSQLDialect.cs
index f536745e..d6e8c2a7 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 @$"DEFAULT E'\\x{convertedString}'";
+ }
return base.Default(defaultValue);
}
diff --git a/src/Migrator/Providers/Impl/PostgreSQL/PostgreSQLTransformationProvider.cs b/src/Migrator/Providers/Impl/PostgreSQL/PostgreSQLTransformationProvider.cs
index 2c7da7fa..34f04aee 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 parse {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/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 0eddfc56..fdb3a0be 100644
--- a/src/Migrator/Providers/Impl/SQLite/SQLiteTransformationProvider.cs
+++ b/src/Migrator/Providers/Impl/SQLite/SQLiteTransformationProvider.cs
@@ -9,6 +9,7 @@
using System.Text.RegularExpressions;
using ForeignKeyConstraint = DotNetProjects.Migrator.Framework.ForeignKeyConstraint;
using Index = DotNetProjects.Migrator.Framework.Index;
+using DotNetProjects.Migrator.Framework.Extensions;
namespace DotNetProjects.Migrator.Providers.Impl.SQLite;
@@ -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,20 @@ public DbType ExtractTypeFromColumnDef(string columnDef)
public override void RemoveForeignKey(string table, string name)
{
- //Check the impl...
- return;
+ 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);
}
public string[] GetCreateIndexSqlStrings(string table)
@@ -404,6 +417,13 @@ public override void RemoveColumn(string tableName, string column)
var sqliteInfoMainTable = GetSQLiteTableInfo(tableName);
+ var checkConstraints = sqliteInfoMainTable.CheckConstraints;
+
+ if (checkConstraints.Any(x => x.CheckConstraintString.Contains(column, StringComparison.OrdinalIgnoreCase)))
+ {
+ 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))
{
throw new MigrationException("Column not found");
@@ -632,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);
@@ -641,15 +666,30 @@ 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));
+ 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
@@ -707,9 +747,11 @@ 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)
+ .Concat(checkConstraintDbFields)
.ToArray();
// ToHashSet() not available in older .NET versions so we create it old-fashioned.
@@ -770,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))
@@ -778,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.");
@@ -789,6 +838,83 @@ 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);
+
+ 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, 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);
+
+ 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))
@@ -840,6 +966,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));
@@ -849,6 +980,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
@@ -859,9 +995,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();
@@ -966,7 +1105,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 +1124,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)
@@ -1119,6 +1262,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)
@@ -1136,6 +1281,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 = [];
@@ -1154,12 +1300,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(')');
@@ -1239,6 +1398,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(@"(?<=\().+(?=\))");
@@ -1273,10 +1437,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)
{
@@ -1385,6 +1559,68 @@ 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))
+ {
+ 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)
diff --git a/src/Migrator/Providers/Impl/SqlServer/SqlServerTransformationProvider.cs b/src/Migrator/Providers/Impl/SqlServer/SqlServerTransformationProvider.cs
index 3cb2568d..e69ebc48 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;
@@ -321,7 +323,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 +331,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)
{ }
@@ -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;
@@ -357,77 +366,187 @@ 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 == "bigint")
+ {
+ column.MigratorDbType = MigratorDbType.Int64;
+ }
+ else if (dataTypeString == "smallint")
{
- var type = reader.GetString(2);
- column.Type = Dialect.GetDbTypeFromString(type);
+ 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")
+ {
+ 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 if (dataTypeString == "real")
+ {
+ column.MigratorDbType = MigratorDbType.Single;
+ }
+ else
+ {
+ throw new NotImplementedException($"The data type '{dataTypeString}' is not implemented yet. Please file an issue.");
+ }
+
if (!reader.IsDBNull(3))
{
column.Size = reader.GetInt32(3);
}
- if (!reader.IsDBNull(4))
- {
- 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 (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)
{
- 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);
- 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);
}
- var d = DateTime.ParseExact(dt, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture);
- column.DefaultValue = d;
+ // We convert to UTC since we restrict date time default values to UTC on default value definition.
+ column.DefaultValue = DateTime.ParseExact(dt, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal);
+ }
+ else
+ {
+ throw new NotImplementedException($"Cannot interpret {column.DefaultValue} in column '{column.Name}' unexpected pattern.");
}
}
else if (column.Type == DbType.Guid)
{
- if (column.DefaultValue is string defVal)
+ column.DefaultValue = Guid.Parse(bracesAndSingleQuoteStrippedString);
+ }
+ else if (column.MigratorDbType == MigratorDbType.Decimal)
+ {
+ // We assume ((1.234))
+ 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 dt = defVal;
- if (defVal.StartsWith("'"))
- {
- dt = defVal.Substring(1, defVal.Length - 2);
- }
+ var hexString = bracesStrippedString.Substring(2);
- var d = Guid.Parse(dt);
- column.DefaultValue = d;
+ // Not available in old .NET version: Convert.FromHexString(hexString);
+
+ 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 '{column.MigratorDbType}'. It is not yet implemented - file an issue.");
}
}
if (!reader.IsDBNull(5))
diff --git a/src/Migrator/Providers/TransformationProvider.cs b/src/Migrator/Providers/TransformationProvider.cs
index b06d0a25..94747d5d 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;
@@ -269,15 +270,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))
+ {
+ throw new MigrationException($"Table '{name}' does not exist");
+ }
+
+ if (!ConstraintExists(table, name))
{
- ExecuteNonQuery(string.Format("ALTER TABLE {0} DROP CONSTRAINT {1}", QuoteTableNameIfRequired(table), QuoteConstraintNameIfRequired(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)
@@ -380,6 +393,11 @@ public virtual void AddView(string name, string tableName, params IViewElement[]
///
public virtual void AddTable(string name, params IDbField[] columns)
{
+ if (this is not SQLiteTransformationProvider && columns.Any(x => x is CheckConstraint))
+ {
+ 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.
AddTable(name, null, columns);
}
@@ -407,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
@@ -429,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);
}
}
@@ -629,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);
}
@@ -649,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);
}