From a3f63b6156deaa1c7269264703a6a093acb3d423 Mon Sep 17 00:00:00 2001 From: JaBistDuNarrisch Date: Tue, 30 Sep 2025 10:24:27 +0200 Subject: [PATCH 1/7] RemoveAllConstraints => obsolete --- src/Migrator/Framework/ITransformationProvider.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Migrator/Framework/ITransformationProvider.cs b/src/Migrator/Framework/ITransformationProvider.cs index 477d61a5..662813a6 100644 --- a/src/Migrator/Framework/ITransformationProvider.cs +++ b/src/Migrator/Framework/ITransformationProvider.cs @@ -518,6 +518,7 @@ public interface ITransformationProvider : IDisposable /// Removes PK, FKs, Unique and CHECK constraints. /// /// + [Obsolete("Drop all constraints separately.")] void RemoveAllConstraints(string table); /// From 75bbce31ba081d9a19f18187f408afcf1c97f3b4 Mon Sep 17 00:00:00 2001 From: JaBistDuNarrisch Date: Tue, 30 Sep 2025 14:58:08 +0200 Subject: [PATCH 2/7] UpdateFromTableToTable for SQLite, Postgre, Oracle and SQL Server --- .../Framework/ITransformationProvider.cs | 10 ++++ .../Oracle/OracleTransformationProvider.cs | 47 +++++++++++++++++++ .../PostgreSQLTransformationProvider.cs | 47 +++++++++++++++++++ .../SQLite/SQLiteTransformationProvider.cs | 47 +++++++++++++++++++ .../SqlServerTransformationProvider.cs | 47 +++++++++++++++++++ src/Migrator/Providers/Models/ColumnPair.cs | 17 +++++++ .../Providers/NoOpTransformationProvider.cs | 6 +++ .../Providers/TransformationProvider.cs | 6 +++ 8 files changed, 227 insertions(+) create mode 100644 src/Migrator/Providers/Models/ColumnPair.cs diff --git a/src/Migrator/Framework/ITransformationProvider.cs b/src/Migrator/Framework/ITransformationProvider.cs index 662813a6..0a5d0dd6 100644 --- a/src/Migrator/Framework/ITransformationProvider.cs +++ b/src/Migrator/Framework/ITransformationProvider.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Data; +using DotNetProjects.Migrator.Framework.Models; namespace DotNetProjects.Migrator.Framework; @@ -645,6 +646,15 @@ IDataReader SelectComplex(IDbCommand cmd, string table, string[] columns, string int Update(string table, string[] columns, object[] values, string[] whereColumns, object[] whereValues); + /// + /// Updates the target table using data from the source table updating the target table. Make sure to only use primary keys or unique columns in + /// + /// + /// + /// Pairs that represent the name of the source column and the column in the target table to be updated. + /// Pairs that represent the name of the source column and the name of the target tabel used to match the rows. + void UpdateFromTableToTable(string tableSourceNotQuoted, string tableTargetNotQuoted, ColumnPair[] fromSourceToTargetColumnPairs, ColumnPair[] conditionColumnPairs); + /// /// Get a command instance /// diff --git a/src/Migrator/Providers/Impl/Oracle/OracleTransformationProvider.cs b/src/Migrator/Providers/Impl/Oracle/OracleTransformationProvider.cs index 77b50ae3..724ea58e 100644 --- a/src/Migrator/Providers/Impl/Oracle/OracleTransformationProvider.cs +++ b/src/Migrator/Providers/Impl/Oracle/OracleTransformationProvider.cs @@ -1,4 +1,5 @@ using DotNetProjects.Migrator.Framework; +using DotNetProjects.Migrator.Framework.Models; using DotNetProjects.Migrator.Providers.Impl.Oracle.Models; using DotNetProjects.Migrator.Providers.Models; using DotNetProjects.Migrator.Providers.Models.Indexes; @@ -907,6 +908,52 @@ public override bool IndexExists(string table, string name) return Convert.ToInt32(scalar) == 1; } + public override void UpdateFromTableToTable(string tableSourceNotQuoted, string tableTargetNotQuoted, ColumnPair[] fromSourceToTargetColumnPairs, ColumnPair[] conditionColumnPairs) + { + if (!TableExists(tableSourceNotQuoted)) + { + throw new Exception($"Table '{tableSourceNotQuoted}' given in '{nameof(tableSourceNotQuoted)}' does not exist"); + } + + if (!TableExists(tableTargetNotQuoted)) + { + throw new Exception($"Table '{tableTargetNotQuoted}' given in '{nameof(tableTargetNotQuoted)}' does not exist"); + } + + if (fromSourceToTargetColumnPairs.Length == 0) + { + throw new Exception($"{nameof(fromSourceToTargetColumnPairs)} is empty."); + } + + if (fromSourceToTargetColumnPairs.Any(x => string.IsNullOrWhiteSpace(x.ColumnNameSourceNotQuoted) || string.IsNullOrWhiteSpace(x.ColumnNameTargetNotQuoted))) + { + throw new Exception($"One of the strings in {nameof(fromSourceToTargetColumnPairs)} is null or empty"); + } + + if (conditionColumnPairs.Length == 0) + { + throw new Exception($"{nameof(conditionColumnPairs)} is empty."); + } + + if (conditionColumnPairs.Any(x => string.IsNullOrWhiteSpace(x.ColumnNameSourceNotQuoted) || string.IsNullOrWhiteSpace(x.ColumnNameTargetNotQuoted))) + { + throw new Exception($"One of the strings in {nameof(conditionColumnPairs)} is null or empty"); + } + + var tableNameSource = QuoteTableNameIfRequired(tableSourceNotQuoted); + var tableNameTarget = QuoteTableNameIfRequired(tableTargetNotQuoted); + + var assignStrings = fromSourceToTargetColumnPairs.Select(x => $"s.{QuoteColumnNameIfRequired(x.ColumnNameSourceNotQuoted)} = t.{QuoteColumnNameIfRequired(x.ColumnNameTargetNotQuoted)}").ToList(); + + var conditionStrings = conditionColumnPairs.Select(x => $"t.{QuoteColumnNameIfRequired(x.ColumnNameTargetNotQuoted)} = s.{QuoteColumnNameIfRequired(x.ColumnNameSourceNotQuoted)}"); + + var assignStringsJoined = string.Join(", ", assignStrings); + var conditionStringsJoined = string.Join(" AND ", conditionStrings); + + var sql = $"MERGE INTO {tableNameTarget} t ON ({conditionStringsJoined}) WHEN MATCHED THEN UPDATE SET {assignStringsJoined}"; + ExecuteNonQuery(sql); + } + private string SchemaInfoTableName { get diff --git a/src/Migrator/Providers/Impl/PostgreSQL/PostgreSQLTransformationProvider.cs b/src/Migrator/Providers/Impl/PostgreSQL/PostgreSQLTransformationProvider.cs index 11221c75..911897dc 100644 --- a/src/Migrator/Providers/Impl/PostgreSQL/PostgreSQLTransformationProvider.cs +++ b/src/Migrator/Providers/Impl/PostgreSQL/PostgreSQLTransformationProvider.cs @@ -12,6 +12,7 @@ #endregion using DotNetProjects.Migrator.Framework; +using DotNetProjects.Migrator.Framework.Models; using DotNetProjects.Migrator.Providers.Models.Indexes; using DotNetProjects.Migrator.Providers.Models.Indexes.Enums; using System; @@ -815,6 +816,52 @@ public override bool IndexExists(string table, string name) return reader.Read(); } + public override void UpdateFromTableToTable(string tableSourceNotQuoted, string tableTargetNotQuoted, ColumnPair[] fromSourceToTargetColumnPairs, ColumnPair[] conditionColumnPairs) + { + if (!TableExists(tableSourceNotQuoted)) + { + throw new Exception($"Table '{tableSourceNotQuoted}' given in '{nameof(tableSourceNotQuoted)}' does not exist"); + } + + if (!TableExists(tableTargetNotQuoted)) + { + throw new Exception($"Table '{tableTargetNotQuoted}' given in '{nameof(tableTargetNotQuoted)}' does not exist"); + } + + if (fromSourceToTargetColumnPairs.Length == 0) + { + throw new Exception($"{nameof(fromSourceToTargetColumnPairs)} is empty."); + } + + if (fromSourceToTargetColumnPairs.Any(x => string.IsNullOrWhiteSpace(x.ColumnNameSourceNotQuoted) || string.IsNullOrWhiteSpace(x.ColumnNameTargetNotQuoted))) + { + throw new Exception($"One of the strings in {nameof(fromSourceToTargetColumnPairs)} is null or empty"); + } + + if (conditionColumnPairs.Length == 0) + { + throw new Exception($"{nameof(conditionColumnPairs)} is empty."); + } + + if (conditionColumnPairs.Any(x => string.IsNullOrWhiteSpace(x.ColumnNameSourceNotQuoted) || string.IsNullOrWhiteSpace(x.ColumnNameTargetNotQuoted))) + { + throw new Exception($"One of the strings in {nameof(conditionColumnPairs)} is null or empty"); + } + + var tableNameSource = QuoteTableNameIfRequired(tableSourceNotQuoted); + var tableNameTarget = QuoteTableNameIfRequired(tableTargetNotQuoted); + + var assignStrings = fromSourceToTargetColumnPairs.Select(x => $"s.{QuoteColumnNameIfRequired(x.ColumnNameSourceNotQuoted)} = t.{QuoteColumnNameIfRequired(x.ColumnNameTargetNotQuoted)}").ToList(); + + var conditionStrings = conditionColumnPairs.Select(x => $"t.{QuoteColumnNameIfRequired(x.ColumnNameTargetNotQuoted)} = s.{QuoteColumnNameIfRequired(x.ColumnNameSourceNotQuoted)}"); + + var assignStringsJoined = string.Join(", ", assignStrings); + var conditionStringsJoined = string.Join(" AND ", conditionStrings); + + var sql = $"MERGE INTO {tableNameTarget} t ON ({conditionStringsJoined}) WHEN MATCHED THEN UPDATE SET {assignStringsJoined}"; + ExecuteNonQuery(sql); + } + protected override void ConfigureParameterWithValue(IDbDataParameter parameter, int index, object value) { if (value is ushort) diff --git a/src/Migrator/Providers/Impl/SQLite/SQLiteTransformationProvider.cs b/src/Migrator/Providers/Impl/SQLite/SQLiteTransformationProvider.cs index dd5a9349..0c054176 100644 --- a/src/Migrator/Providers/Impl/SQLite/SQLiteTransformationProvider.cs +++ b/src/Migrator/Providers/Impl/SQLite/SQLiteTransformationProvider.cs @@ -12,6 +12,7 @@ using DotNetProjects.Migrator.Framework.Extensions; using DotNetProjects.Migrator.Providers.Models.Indexes; using DotNetProjects.Migrator.Providers.Models.Indexes.Enums; +using DotNetProjects.Migrator.Framework.Models; namespace DotNetProjects.Migrator.Providers.Impl.SQLite; @@ -209,6 +210,52 @@ public override ForeignKeyConstraint[] GetForeignKeyConstraints(string tableName return foreignKeyConstraints.ToArray(); } + public override void UpdateFromTableToTable(string tableSourceNotQuoted, string tableTargetNotQuoted, ColumnPair[] fromSourceToTargetColumnPairs, ColumnPair[] conditionColumnPairs) + { + if (!TableExists(tableSourceNotQuoted)) + { + throw new Exception($"Table '{tableSourceNotQuoted}' given in '{nameof(tableSourceNotQuoted)}' does not exist"); + } + + if (!TableExists(tableTargetNotQuoted)) + { + throw new Exception($"Table '{tableTargetNotQuoted}' given in '{nameof(tableTargetNotQuoted)}' does not exist"); + } + + if (fromSourceToTargetColumnPairs.Length == 0) + { + throw new Exception($"{nameof(fromSourceToTargetColumnPairs)} is empty."); + } + + if (fromSourceToTargetColumnPairs.Any(x => string.IsNullOrWhiteSpace(x.ColumnNameSourceNotQuoted) || string.IsNullOrWhiteSpace(x.ColumnNameTargetNotQuoted))) + { + throw new Exception($"One of the strings in {nameof(fromSourceToTargetColumnPairs)} is null or empty"); + } + + if (conditionColumnPairs.Length == 0) + { + throw new Exception($"{nameof(conditionColumnPairs)} is empty."); + } + + if (conditionColumnPairs.Any(x => string.IsNullOrWhiteSpace(x.ColumnNameSourceNotQuoted) || string.IsNullOrWhiteSpace(x.ColumnNameTargetNotQuoted))) + { + throw new Exception($"One of the strings in {nameof(conditionColumnPairs)} is null or empty"); + } + + var tableNameSource = QuoteTableNameIfRequired(tableSourceNotQuoted); + var tableNameTarget = QuoteTableNameIfRequired(tableTargetNotQuoted); + + var assignStrings = fromSourceToTargetColumnPairs.Select(x => $"{QuoteColumnNameIfRequired(x.ColumnNameSourceNotQuoted)} = {tableNameSource}.{QuoteColumnNameIfRequired(x.ColumnNameSourceNotQuoted)}").ToList(); + + var conditionStrings = conditionColumnPairs.Select(x => $"{tableNameSource}.{QuoteColumnNameIfRequired(x.ColumnNameSourceNotQuoted)} = {tableNameTarget}.{QuoteColumnNameIfRequired(x.ColumnNameTargetNotQuoted)}"); + + var assignStringsJoined = string.Join(", ", assignStrings); + var conditionStringsJoined = string.Join(" AND ", conditionStrings); + + var sql = $"UPDATE {tableNameTarget} SET {assignStringsJoined} FROM {tableNameSource} WHERE {conditionStringsJoined}"; + ExecuteNonQuery(sql); + } + private List GetForeignKeyListItems(string tableNameNotQuoted) { List pragmaForeignKeyListItems = []; diff --git a/src/Migrator/Providers/Impl/SqlServer/SqlServerTransformationProvider.cs b/src/Migrator/Providers/Impl/SqlServer/SqlServerTransformationProvider.cs index 59b63ab5..28a34812 100644 --- a/src/Migrator/Providers/Impl/SqlServer/SqlServerTransformationProvider.cs +++ b/src/Migrator/Providers/Impl/SqlServer/SqlServerTransformationProvider.cs @@ -12,6 +12,7 @@ #endregion using DotNetProjects.Migrator.Framework; +using DotNetProjects.Migrator.Framework.Models; using DotNetProjects.Migrator.Providers.Models.Indexes; using System; using System.Collections.Generic; @@ -800,6 +801,52 @@ public override void RenameTable(string oldName, string newName) ExecuteNonQuery(string.Format("EXEC sp_rename '{0}', '{1}'", oldName, newName)); } + public override void UpdateFromTableToTable(string tableSourceNotQuoted, string tableTargetNotQuoted, ColumnPair[] fromSourceToTargetColumnPairs, ColumnPair[] conditionColumnPairs) + { + if (!TableExists(tableSourceNotQuoted)) + { + throw new Exception($"Table '{tableSourceNotQuoted}' given in '{nameof(tableSourceNotQuoted)}' does not exist"); + } + + if (!TableExists(tableTargetNotQuoted)) + { + throw new Exception($"Table '{tableTargetNotQuoted}' given in '{nameof(tableTargetNotQuoted)}' does not exist"); + } + + if (fromSourceToTargetColumnPairs.Length == 0) + { + throw new Exception($"{nameof(fromSourceToTargetColumnPairs)} is empty."); + } + + if (fromSourceToTargetColumnPairs.Any(x => string.IsNullOrWhiteSpace(x.ColumnNameSourceNotQuoted) || string.IsNullOrWhiteSpace(x.ColumnNameTargetNotQuoted))) + { + throw new Exception($"One of the strings in {nameof(fromSourceToTargetColumnPairs)} is null or empty"); + } + + if (conditionColumnPairs.Length == 0) + { + throw new Exception($"{nameof(conditionColumnPairs)} is empty."); + } + + if (conditionColumnPairs.Any(x => string.IsNullOrWhiteSpace(x.ColumnNameSourceNotQuoted) || string.IsNullOrWhiteSpace(x.ColumnNameTargetNotQuoted))) + { + throw new Exception($"One of the strings in {nameof(conditionColumnPairs)} is null or empty"); + } + + var tableNameSource = QuoteTableNameIfRequired(tableSourceNotQuoted); + var tableNameTarget = QuoteTableNameIfRequired(tableTargetNotQuoted); + + var assignStrings = fromSourceToTargetColumnPairs.Select(x => $"s.{QuoteColumnNameIfRequired(x.ColumnNameSourceNotQuoted)} = t.{QuoteColumnNameIfRequired(x.ColumnNameTargetNotQuoted)}").ToList(); + + var conditionStrings = conditionColumnPairs.Select(x => $"t.{QuoteColumnNameIfRequired(x.ColumnNameTargetNotQuoted)} = s.{QuoteColumnNameIfRequired(x.ColumnNameSourceNotQuoted)}"); + + var assignStringsJoined = string.Join(", ", assignStrings); + var conditionStringsJoined = string.Join(" AND ", conditionStrings); + + var sql = $"MERGE INTO {tableNameTarget} t ON ({conditionStringsJoined}) WHEN MATCHED THEN UPDATE SET {assignStringsJoined}"; + ExecuteNonQuery(sql); + } + // Deletes all constraints linked to a column. Sql Server // doesn't seems to do this. private void DeleteColumnConstraints(string table, string column) diff --git a/src/Migrator/Providers/Models/ColumnPair.cs b/src/Migrator/Providers/Models/ColumnPair.cs new file mode 100644 index 00000000..6aa5e259 --- /dev/null +++ b/src/Migrator/Providers/Models/ColumnPair.cs @@ -0,0 +1,17 @@ +namespace DotNetProjects.Migrator.Framework.Models; + +/// +/// Represents a column pair for usage e.g. in a column comparison. +/// +public class ColumnPair +{ + /// + /// Gets or sets the column name of the source table. Use the not quoted column name. + /// + public string ColumnNameSourceNotQuoted { get; set; } + + /// + /// Gets or sets the column name of the target table. Use the not quoted column name. + /// + public string ColumnNameTargetNotQuoted { get; set; } +} \ No newline at end of file diff --git a/src/Migrator/Providers/NoOpTransformationProvider.cs b/src/Migrator/Providers/NoOpTransformationProvider.cs index 8da1b9b3..387ed10d 100644 --- a/src/Migrator/Providers/NoOpTransformationProvider.cs +++ b/src/Migrator/Providers/NoOpTransformationProvider.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Data; using DotNetProjects.Migrator.Framework; +using DotNetProjects.Migrator.Framework.Models; using DotNetProjects.Migrator.Framework.SchemaBuilder; using ForeignKeyConstraint = DotNetProjects.Migrator.Framework.ForeignKeyConstraint; using Index = DotNetProjects.Migrator.Framework.Index; @@ -591,4 +592,9 @@ public void AddColumn(string table, string column, MigratorDbType type, object d { throw new NotImplementedException(); } + + public void UpdateFromTableToTable(string tableSourceNotQuoted, string tableTargetNotQuoted, ColumnPair[] fromSourceToTargetColumnPairs, ColumnPair[] conditionColumnPairs) + { + throw new NotImplementedException(); + } } diff --git a/src/Migrator/Providers/TransformationProvider.cs b/src/Migrator/Providers/TransformationProvider.cs index 465af5a5..86b5ab04 100644 --- a/src/Migrator/Providers/TransformationProvider.cs +++ b/src/Migrator/Providers/TransformationProvider.cs @@ -13,6 +13,7 @@ using DotNetProjects.Migrator.Framework; using DotNetProjects.Migrator.Framework.Loggers; +using DotNetProjects.Migrator.Framework.Models; using DotNetProjects.Migrator.Framework.SchemaBuilder; using DotNetProjects.Migrator.Providers.Impl.SQLite; using DotNetProjects.Migrator.Providers.Models; @@ -1331,6 +1332,11 @@ public virtual int Update(string table, string[] columns, object[] values, strin return command.ExecuteNonQuery(); } + public virtual void UpdateFromTableToTable(string tableSourceNotQuoted, string tableTargetNotQuoted, ColumnPair[] fromSourceToTargetColumnPairs, ColumnPair[] conditionColumnPairs) + { + throw new NotImplementedException(); + } + public virtual int Insert(string table, string[] columns, object[] values) { if (string.IsNullOrEmpty(table)) From 689ea62ccdf65a0177a7571aa931fab3357bb7f1 Mon Sep 17 00:00:00 2001 From: JaBistDuNarrisch Date: Tue, 30 Sep 2025 17:14:13 +0200 Subject: [PATCH 3/7] UpdateFromTableToTable Tests --- ...Generic_UpdateFromTableToTableTestsBase.cs | 103 ++++++++++++++++++ .../Models/UpdateFromTableToTableModel.cs | 10 ++ ...racleTransformationProviderGenericTests.cs | 70 +++++------- ...ionProvider_UpdateFromTableToTableTests.cs | 16 +++ ...ionProvider_UpdateFromTableToTableTests.cs | 16 +++ ...ionProvider_UpdateFromTableToTableTests.cs | 16 +++ ...ionProvider_UpdateFromTableToTableTests.cs | 16 +++ .../Oracle/OracleTransformationProvider.cs | 8 +- .../PostgreSQLTransformationProvider.cs | 8 +- .../SQLite/SQLiteTransformationProvider.cs | 2 +- .../SqlServerTransformationProvider.cs | 8 +- 11 files changed, 220 insertions(+), 53 deletions(-) create mode 100644 src/Migrator.Tests/Providers/Generic/Generic_UpdateFromTableToTableTestsBase.cs create mode 100644 src/Migrator.Tests/Providers/Generic/Models/UpdateFromTableToTableModel.cs create mode 100644 src/Migrator.Tests/Providers/OracleProvider/OracleTransformationProvider_UpdateFromTableToTableTests.cs create mode 100644 src/Migrator.Tests/Providers/PostgreSQL/PostgresSQLTransformationProvider_UpdateFromTableToTableTests.cs create mode 100644 src/Migrator.Tests/Providers/SQLServer/SQLServerTransformationProvider_UpdateFromTableToTableTests.cs create mode 100644 src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_UpdateFromTableToTableTests.cs diff --git a/src/Migrator.Tests/Providers/Generic/Generic_UpdateFromTableToTableTestsBase.cs b/src/Migrator.Tests/Providers/Generic/Generic_UpdateFromTableToTableTestsBase.cs new file mode 100644 index 00000000..fac1ca91 --- /dev/null +++ b/src/Migrator.Tests/Providers/Generic/Generic_UpdateFromTableToTableTestsBase.cs @@ -0,0 +1,103 @@ +using System.Collections.Generic; +using System.Data; +using DotNetProjects.Migrator.Framework; +using DotNetProjects.Migrator.Framework.Models; +using Migrator.Tests.Providers.Base; +using Migrator.Tests.Providers.Generic.Models; +using NUnit.Framework; + +namespace Migrator.Tests.Providers.Generic; + +public abstract class Generic_UpdateFromTableToTableTestsBase : TransformationProviderBase +{ + [Test] + public void UpdateFromTableToTable_Success() + { + // Arrange + const string tableNameSource = "TableSource"; + const string tableNameTarget = "TableTarget"; + const string columnName1Source = "ColumnName1Source"; + const string columnName2Source = "ColumnName2Source"; + const string columnName3Source = "ColumnName3Source"; + const string columnName4Source = "ColumnName4Source"; + const string columnName5Source = "ColumnName5Source"; + + const string columnName1Target = "ColumnName1Target"; + const string columnName2Target = "ColumnName2Target"; + const string columnName3Target = "ColumnName3Target"; + const string columnName4Target = "ColumnName4Target"; + const string columnName5Target = "ColumnName5Target"; + + + Provider.AddTable(tableNameSource, + new Column(columnName1Source, DbType.Int32, ColumnProperty.NotNull), + new Column(columnName2Source, DbType.Int32, ColumnProperty.NotNull), + new Column(columnName3Source, DbType.String), + new Column(columnName4Source, DbType.String), + new Column(columnName5Source, DbType.String) + ); + + Provider.AddPrimaryKey("PK_Source", tableNameSource, [columnName1Source, columnName2Source]); + + Provider.AddTable(tableNameTarget, + new Column(columnName1Target, DbType.Int32, ColumnProperty.NotNull), + new Column(columnName2Target, DbType.Int32, ColumnProperty.NotNull), + new Column(columnName3Target, DbType.String), + new Column(columnName4Target, DbType.String), + new Column(columnName5Target, DbType.String) + ); + + Provider.AddPrimaryKey("PK_Target", tableNameTarget, [columnName1Target, columnName2Target]); + + Provider.Insert(tableNameSource, [columnName1Source, columnName2Source, columnName3Source, columnName4Source, columnName5Source], [1, 2, "source 1", "source 2", "source 3"]); + Provider.Insert(tableNameSource, [columnName1Source, columnName2Source, columnName3Source, columnName4Source, columnName5Source], [2, 3, "source 11", "source 22", "source 33"]); + + Provider.Insert(tableNameTarget, [columnName1Target, columnName2Target, columnName3Target, columnName4Target, columnName5Target], [1, 2, "target 1", "target 2", "target 3"]); + Provider.Insert(tableNameTarget, [columnName1Target, columnName2Target, columnName3Target, columnName4Target, columnName5Target], [1, 3, "target no update", "target no update", "target no update"]); + + // Act + Provider.UpdateFromTableToTable( + tableNameSource, + tableNameTarget, + [ + new ColumnPair { ColumnNameSourceNotQuoted = columnName3Source, ColumnNameTargetNotQuoted = columnName3Target }, + new ColumnPair { ColumnNameSourceNotQuoted = columnName4Source, ColumnNameTargetNotQuoted = columnName4Target }, + new ColumnPair { ColumnNameSourceNotQuoted = columnName5Source, ColumnNameTargetNotQuoted = columnName5Target } + ], + [ + new ColumnPair { ColumnNameSourceNotQuoted = columnName1Source, ColumnNameTargetNotQuoted = columnName1Target }, + new ColumnPair { ColumnNameSourceNotQuoted = columnName2Source, ColumnNameTargetNotQuoted = columnName2Target } + ] + ); + + // Assert + List targetRows = []; + using (var cmd = Provider.CreateCommand()) + using (var reader = Provider.Select(cmd, tableNameTarget, [columnName1Target, columnName2Target, columnName3Target, columnName4Target, columnName5Target])) + { + while (reader.Read()) + { + targetRows.Add(new UpdateFromTableToTableModel + { + Column1 = reader.GetInt32(0), + Column2 = reader.GetInt32(1), + Column3 = reader.GetString(2), + Column4 = reader.GetString(3), + Column5 = reader.GetString(4) + }); + } + } + + List expectedTargetRows = [ + new UpdateFromTableToTableModel{ Column1 = 1, Column2 = 2, Column3 = "source 1", Column4 = "source 2", Column5 = "source 3"}, + new UpdateFromTableToTableModel{ Column1 = 1, Column2 = 3, Column3 = "target no update", Column4 = "target no update", Column5 = "target no update"}, + ]; + + Assert.That(targetRows, Is.EquivalentTo(expectedTargetRows).Using((x, y) => + x.Column1 == y.Column1 && + x.Column2 == y.Column2 && + x.Column3 == y.Column3 && + x.Column4 == y.Column4 && + x.Column5 == y.Column5)); + } +} \ No newline at end of file diff --git a/src/Migrator.Tests/Providers/Generic/Models/UpdateFromTableToTableModel.cs b/src/Migrator.Tests/Providers/Generic/Models/UpdateFromTableToTableModel.cs new file mode 100644 index 00000000..6f99f0c5 --- /dev/null +++ b/src/Migrator.Tests/Providers/Generic/Models/UpdateFromTableToTableModel.cs @@ -0,0 +1,10 @@ +namespace Migrator.Tests.Providers.Generic.Models; + +public class UpdateFromTableToTableModel +{ + public int Column1 { get; set; } + public int Column2 { get; set; } + public string Column3 { get; set; } + public string Column4 { get; set; } + public string Column5 { get; set; } +} \ No newline at end of file diff --git a/src/Migrator.Tests/Providers/OracleProvider/OracleTransformationProviderGenericTests.cs b/src/Migrator.Tests/Providers/OracleProvider/OracleTransformationProviderGenericTests.cs index d7ffcdce..5f56b686 100644 --- a/src/Migrator.Tests/Providers/OracleProvider/OracleTransformationProviderGenericTests.cs +++ b/src/Migrator.Tests/Providers/OracleProvider/OracleTransformationProviderGenericTests.cs @@ -1,40 +1,30 @@ -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)); - } -} +using System.Data; +using System.Threading.Tasks; +using DotNetProjects.Migrator.Framework; +using Migrator.Tests.Providers.Generic; +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/OracleTransformationProvider_UpdateFromTableToTableTests.cs b/src/Migrator.Tests/Providers/OracleProvider/OracleTransformationProvider_UpdateFromTableToTableTests.cs new file mode 100644 index 00000000..a8d297d6 --- /dev/null +++ b/src/Migrator.Tests/Providers/OracleProvider/OracleTransformationProvider_UpdateFromTableToTableTests.cs @@ -0,0 +1,16 @@ +using System.Threading.Tasks; +using Migrator.Tests.Providers.Generic; +using NUnit.Framework; + +namespace Migrator.Tests.Providers.OracleProvider; + +[TestFixture] +[Category("Oracle")] +public class OracleTransformationProvider_UpdateFromTableToTableTests : Generic_UpdateFromTableToTableTestsBase +{ + [SetUp] + public async Task SetUpAsync() + { + await BeginOracleTransactionAsync(); + } +} \ No newline at end of file diff --git a/src/Migrator.Tests/Providers/PostgreSQL/PostgresSQLTransformationProvider_UpdateFromTableToTableTests.cs b/src/Migrator.Tests/Providers/PostgreSQL/PostgresSQLTransformationProvider_UpdateFromTableToTableTests.cs new file mode 100644 index 00000000..2b39e27f --- /dev/null +++ b/src/Migrator.Tests/Providers/PostgreSQL/PostgresSQLTransformationProvider_UpdateFromTableToTableTests.cs @@ -0,0 +1,16 @@ +using System.Threading.Tasks; +using Migrator.Tests.Providers.Generic; +using NUnit.Framework; + +namespace Migrator.Tests.Providers.PostgreSQL; + +[TestFixture] +[Category("Postgre")] +public class PostgreSQLTransformationProvider_UpdateFromTableToTableTests : Generic_UpdateFromTableToTableTestsBase +{ + [SetUp] + public async Task SetUpAsync() + { + await BeginPostgreSQLTransactionAsync(); + } +} diff --git a/src/Migrator.Tests/Providers/SQLServer/SQLServerTransformationProvider_UpdateFromTableToTableTests.cs b/src/Migrator.Tests/Providers/SQLServer/SQLServerTransformationProvider_UpdateFromTableToTableTests.cs new file mode 100644 index 00000000..7015e781 --- /dev/null +++ b/src/Migrator.Tests/Providers/SQLServer/SQLServerTransformationProvider_UpdateFromTableToTableTests.cs @@ -0,0 +1,16 @@ +using System.Threading.Tasks; +using Migrator.Tests.Providers.Generic; +using NUnit.Framework; + +namespace Migrator.Tests.Providers.SQLServer; + +[TestFixture] +[Category("SqlServer")] +public class SQLServerTransformationProvider_UpdateFromTableToTableTests : Generic_UpdateFromTableToTableTestsBase +{ + [SetUp] + public async Task SetUpAsync() + { + await BeginSQLServerTransactionAsync(); + } +} diff --git a/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_UpdateFromTableToTableTests.cs b/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_UpdateFromTableToTableTests.cs new file mode 100644 index 00000000..aac17b01 --- /dev/null +++ b/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_UpdateFromTableToTableTests.cs @@ -0,0 +1,16 @@ +using System.Threading.Tasks; +using Migrator.Tests.Providers.Generic; +using NUnit.Framework; + +namespace Migrator.Tests.Providers.SQLite; + +[TestFixture] +[Category("SQLite")] +public class SQLiteTransformationProvider_UpdateFromTableToTableTests : Generic_UpdateFromTableToTableTestsBase +{ + [SetUp] + public async Task SetUpAsync() + { + await BeginSQLiteTransactionAsync(); + } +} \ No newline at end of file diff --git a/src/Migrator/Providers/Impl/Oracle/OracleTransformationProvider.cs b/src/Migrator/Providers/Impl/Oracle/OracleTransformationProvider.cs index 724ea58e..b3d82828 100644 --- a/src/Migrator/Providers/Impl/Oracle/OracleTransformationProvider.cs +++ b/src/Migrator/Providers/Impl/Oracle/OracleTransformationProvider.cs @@ -943,14 +943,14 @@ public override void UpdateFromTableToTable(string tableSourceNotQuoted, string var tableNameSource = QuoteTableNameIfRequired(tableSourceNotQuoted); var tableNameTarget = QuoteTableNameIfRequired(tableTargetNotQuoted); - var assignStrings = fromSourceToTargetColumnPairs.Select(x => $"s.{QuoteColumnNameIfRequired(x.ColumnNameSourceNotQuoted)} = t.{QuoteColumnNameIfRequired(x.ColumnNameTargetNotQuoted)}").ToList(); - var conditionStrings = conditionColumnPairs.Select(x => $"t.{QuoteColumnNameIfRequired(x.ColumnNameTargetNotQuoted)} = s.{QuoteColumnNameIfRequired(x.ColumnNameSourceNotQuoted)}"); - var assignStringsJoined = string.Join(", ", assignStrings); + var assignStrings = fromSourceToTargetColumnPairs.Select(x => $"{QuoteColumnNameIfRequired(x.ColumnNameTargetNotQuoted)} = s.{QuoteColumnNameIfRequired(x.ColumnNameSourceNotQuoted)}").ToList(); + var conditionStringsJoined = string.Join(" AND ", conditionStrings); + var assignStringsJoined = string.Join(", ", assignStrings); - var sql = $"MERGE INTO {tableNameTarget} t ON ({conditionStringsJoined}) WHEN MATCHED THEN UPDATE SET {assignStringsJoined}"; + var sql = $"MERGE INTO {tableNameTarget} t USING {tableNameSource} s ON ({conditionStringsJoined}) WHEN MATCHED THEN UPDATE SET {assignStringsJoined}"; ExecuteNonQuery(sql); } diff --git a/src/Migrator/Providers/Impl/PostgreSQL/PostgreSQLTransformationProvider.cs b/src/Migrator/Providers/Impl/PostgreSQL/PostgreSQLTransformationProvider.cs index 911897dc..f0fe28b5 100644 --- a/src/Migrator/Providers/Impl/PostgreSQL/PostgreSQLTransformationProvider.cs +++ b/src/Migrator/Providers/Impl/PostgreSQL/PostgreSQLTransformationProvider.cs @@ -851,14 +851,14 @@ public override void UpdateFromTableToTable(string tableSourceNotQuoted, string var tableNameSource = QuoteTableNameIfRequired(tableSourceNotQuoted); var tableNameTarget = QuoteTableNameIfRequired(tableTargetNotQuoted); - var assignStrings = fromSourceToTargetColumnPairs.Select(x => $"s.{QuoteColumnNameIfRequired(x.ColumnNameSourceNotQuoted)} = t.{QuoteColumnNameIfRequired(x.ColumnNameTargetNotQuoted)}").ToList(); - var conditionStrings = conditionColumnPairs.Select(x => $"t.{QuoteColumnNameIfRequired(x.ColumnNameTargetNotQuoted)} = s.{QuoteColumnNameIfRequired(x.ColumnNameSourceNotQuoted)}"); - var assignStringsJoined = string.Join(", ", assignStrings); + var assignStrings = fromSourceToTargetColumnPairs.Select(x => $"{QuoteColumnNameIfRequired(x.ColumnNameTargetNotQuoted)} = s.{QuoteColumnNameIfRequired(x.ColumnNameSourceNotQuoted)}").ToList(); + var conditionStringsJoined = string.Join(" AND ", conditionStrings); + var assignStringsJoined = string.Join(", ", assignStrings); - var sql = $"MERGE INTO {tableNameTarget} t ON ({conditionStringsJoined}) WHEN MATCHED THEN UPDATE SET {assignStringsJoined}"; + var sql = $"MERGE INTO {tableNameTarget} t USING {tableNameSource} s ON ({conditionStringsJoined}) WHEN MATCHED THEN UPDATE SET {assignStringsJoined}"; ExecuteNonQuery(sql); } diff --git a/src/Migrator/Providers/Impl/SQLite/SQLiteTransformationProvider.cs b/src/Migrator/Providers/Impl/SQLite/SQLiteTransformationProvider.cs index 0c054176..37c7bbf5 100644 --- a/src/Migrator/Providers/Impl/SQLite/SQLiteTransformationProvider.cs +++ b/src/Migrator/Providers/Impl/SQLite/SQLiteTransformationProvider.cs @@ -245,7 +245,7 @@ public override void UpdateFromTableToTable(string tableSourceNotQuoted, string var tableNameSource = QuoteTableNameIfRequired(tableSourceNotQuoted); var tableNameTarget = QuoteTableNameIfRequired(tableTargetNotQuoted); - var assignStrings = fromSourceToTargetColumnPairs.Select(x => $"{QuoteColumnNameIfRequired(x.ColumnNameSourceNotQuoted)} = {tableNameSource}.{QuoteColumnNameIfRequired(x.ColumnNameSourceNotQuoted)}").ToList(); + var assignStrings = fromSourceToTargetColumnPairs.Select(x => $"{QuoteColumnNameIfRequired(x.ColumnNameTargetNotQuoted)} = {tableNameSource}.{QuoteColumnNameIfRequired(x.ColumnNameSourceNotQuoted)}").ToList(); var conditionStrings = conditionColumnPairs.Select(x => $"{tableNameSource}.{QuoteColumnNameIfRequired(x.ColumnNameSourceNotQuoted)} = {tableNameTarget}.{QuoteColumnNameIfRequired(x.ColumnNameTargetNotQuoted)}"); diff --git a/src/Migrator/Providers/Impl/SqlServer/SqlServerTransformationProvider.cs b/src/Migrator/Providers/Impl/SqlServer/SqlServerTransformationProvider.cs index 28a34812..3b649356 100644 --- a/src/Migrator/Providers/Impl/SqlServer/SqlServerTransformationProvider.cs +++ b/src/Migrator/Providers/Impl/SqlServer/SqlServerTransformationProvider.cs @@ -836,14 +836,14 @@ public override void UpdateFromTableToTable(string tableSourceNotQuoted, string var tableNameSource = QuoteTableNameIfRequired(tableSourceNotQuoted); var tableNameTarget = QuoteTableNameIfRequired(tableTargetNotQuoted); - var assignStrings = fromSourceToTargetColumnPairs.Select(x => $"s.{QuoteColumnNameIfRequired(x.ColumnNameSourceNotQuoted)} = t.{QuoteColumnNameIfRequired(x.ColumnNameTargetNotQuoted)}").ToList(); - var conditionStrings = conditionColumnPairs.Select(x => $"t.{QuoteColumnNameIfRequired(x.ColumnNameTargetNotQuoted)} = s.{QuoteColumnNameIfRequired(x.ColumnNameSourceNotQuoted)}"); - var assignStringsJoined = string.Join(", ", assignStrings); + var assignStrings = fromSourceToTargetColumnPairs.Select(x => $"{QuoteColumnNameIfRequired(x.ColumnNameTargetNotQuoted)} = s.{QuoteColumnNameIfRequired(x.ColumnNameSourceNotQuoted)}").ToList(); + var conditionStringsJoined = string.Join(" AND ", conditionStrings); + var assignStringsJoined = string.Join(", ", assignStrings); - var sql = $"MERGE INTO {tableNameTarget} t ON ({conditionStringsJoined}) WHEN MATCHED THEN UPDATE SET {assignStringsJoined}"; + var sql = $"MERGE INTO {tableNameTarget} t USING {tableNameSource} s ON ({conditionStringsJoined}) WHEN MATCHED THEN UPDATE SET {assignStringsJoined};"; ExecuteNonQuery(sql); } From 6c4883f6aecdd02cb9f204c09ec9ff884f791f67 Mon Sep 17 00:00:00 2001 From: JaBistDuNarrisch Date: Tue, 30 Sep 2025 17:34:39 +0200 Subject: [PATCH 4/7] Version 13 Postgre => Merge supported in 15+ => Downgrade to UPDATE...FROM --- .../Impl/PostgreSQL/PostgreSQLTransformationProvider.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Migrator/Providers/Impl/PostgreSQL/PostgreSQLTransformationProvider.cs b/src/Migrator/Providers/Impl/PostgreSQL/PostgreSQLTransformationProvider.cs index f0fe28b5..74d46083 100644 --- a/src/Migrator/Providers/Impl/PostgreSQL/PostgreSQLTransformationProvider.cs +++ b/src/Migrator/Providers/Impl/PostgreSQL/PostgreSQLTransformationProvider.cs @@ -851,14 +851,14 @@ public override void UpdateFromTableToTable(string tableSourceNotQuoted, string var tableNameSource = QuoteTableNameIfRequired(tableSourceNotQuoted); var tableNameTarget = QuoteTableNameIfRequired(tableTargetNotQuoted); - var conditionStrings = conditionColumnPairs.Select(x => $"t.{QuoteColumnNameIfRequired(x.ColumnNameTargetNotQuoted)} = s.{QuoteColumnNameIfRequired(x.ColumnNameSourceNotQuoted)}"); + var assignStrings = fromSourceToTargetColumnPairs.Select(x => $"{QuoteColumnNameIfRequired(x.ColumnNameTargetNotQuoted)} = {tableNameSource}.{QuoteColumnNameIfRequired(x.ColumnNameSourceNotQuoted)}").ToList(); - var assignStrings = fromSourceToTargetColumnPairs.Select(x => $"{QuoteColumnNameIfRequired(x.ColumnNameTargetNotQuoted)} = s.{QuoteColumnNameIfRequired(x.ColumnNameSourceNotQuoted)}").ToList(); + var conditionStrings = conditionColumnPairs.Select(x => $"{tableNameSource}.{QuoteColumnNameIfRequired(x.ColumnNameSourceNotQuoted)} = {tableNameTarget}.{QuoteColumnNameIfRequired(x.ColumnNameTargetNotQuoted)}"); - var conditionStringsJoined = string.Join(" AND ", conditionStrings); var assignStringsJoined = string.Join(", ", assignStrings); + var conditionStringsJoined = string.Join(" AND ", conditionStrings); - var sql = $"MERGE INTO {tableNameTarget} t USING {tableNameSource} s ON ({conditionStringsJoined}) WHEN MATCHED THEN UPDATE SET {assignStringsJoined}"; + var sql = $"UPDATE {tableNameTarget} SET {assignStringsJoined} FROM {tableNameSource} WHERE {conditionStringsJoined}"; ExecuteNonQuery(sql); } From 42935877a25f99edcbda515329ca729a2d2e8372 Mon Sep 17 00:00:00 2001 From: JaBistDuNarrisch Date: Tue, 30 Sep 2025 17:49:24 +0200 Subject: [PATCH 5/7] remove minimum pool size --- src/Migrator.Tests/appsettings.json | 46 ++++++++++++++--------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/Migrator.Tests/appsettings.json b/src/Migrator.Tests/appsettings.json index d2710b6f..a5e74631 100644 --- a/src/Migrator.Tests/appsettings.json +++ b/src/Migrator.Tests/appsettings.json @@ -1,24 +1,24 @@ -{ - "DatabaseConnectionConfigs": [ - { - "Id": "SQLite", - "ConnectionString": "Data Source=:memory:;version=3" - }, - { - "Id": "SQLServer", - "ConnectionString": "Data Source=localhost;Initial Catalog=Whatever;user=sa;pwd=YourStrong@Passw0rd;encrypt=false" - }, - { - "Id": "PostgreSQL", - "ConnectionString": "Server=localhost;Port=5432;Database=postgres;User Id=testuser;Password=testpass;Pooling=true;Minimum Pool Size=100" - }, - { - "Id": "Oracle", - "ConnectionString": "Data Source=//localhost:1521/FREEPDB1;User Id=k;Password=k;" - }, - { - "Id": "MySQL", - "ConnectionString": "Server=127.0.0.1;Port=3306;Database=testdb;User Id=testuser;Password=testpass;" - } - ] +{ + "DatabaseConnectionConfigs": [ + { + "Id": "SQLite", + "ConnectionString": "Data Source=:memory:;version=3" + }, + { + "Id": "SQLServer", + "ConnectionString": "Data Source=localhost;Initial Catalog=Whatever;user=sa;pwd=YourStrong@Passw0rd;encrypt=false" + }, + { + "Id": "PostgreSQL", + "ConnectionString": "Server=localhost;Port=5432;Database=postgres;User Id=testuser;Password=testpass;Pooling=true;" + }, + { + "Id": "Oracle", + "ConnectionString": "Data Source=//localhost:1521/FREEPDB1;User Id=k;Password=k;" + }, + { + "Id": "MySQL", + "ConnectionString": "Server=127.0.0.1;Port=3306;Database=testdb;User Id=testuser;Password=testpass;" + } + ] } \ No newline at end of file From b62c0ec5229ae7d17a756859cabb000c237a27af Mon Sep 17 00:00:00 2001 From: JaBistDuNarrisch Date: Tue, 30 Sep 2025 18:37:44 +0200 Subject: [PATCH 6/7] Removed if (!String.IsNullOrEmpty(connectionString)) --- src/Migrator.Tests/ProviderFactoryTest.cs | 194 ++++++++++------------ 1 file changed, 92 insertions(+), 102 deletions(-) diff --git a/src/Migrator.Tests/ProviderFactoryTest.cs b/src/Migrator.Tests/ProviderFactoryTest.cs index dce3dcef..8687b4ef 100644 --- a/src/Migrator.Tests/ProviderFactoryTest.cs +++ b/src/Migrator.Tests/ProviderFactoryTest.cs @@ -1,102 +1,92 @@ -using System; -using System.Configuration; -using System.Diagnostics; -using System.Linq; -using DotNetProjects.Migrator; -using DotNetProjects.Migrator.Providers; -using Migrator.Tests.Settings; -using Migrator.Tests.Settings.Config; -using Npgsql; -using NUnit.Framework; - -namespace Migrator.Tests; - -[TestFixture] -public class ProviderFactoryTest -{ - [Test] - public void CanGetDialectsForProvider() - { - foreach (var provider in Enum.GetValues(typeof(ProviderTypes)).Cast().Where(x => x != ProviderTypes.none)) - { - Assert.That(ProviderFactory.DialectForProvider(provider), Is.Not.Null); - } - - Assert.That(ProviderFactory.DialectForProvider(ProviderTypes.none), Is.Null); - } - - [SetUp] - public void SetUp() - { - DbProviderFactories.RegisterFactory("Npgsql", () => NpgsqlFactory.Instance); - DbProviderFactories.RegisterFactory("MySql.Data.MySqlClient", () => MySql.Data.MySqlClient.MySqlClientFactory.Instance); - DbProviderFactories.RegisterFactory("Oracle.DataAccess.Client", () => Oracle.ManagedDataAccess.Client.OracleClientFactory.Instance); - DbProviderFactories.RegisterFactory("System.Data.SqlClient", () => Microsoft.Data.SqlClient.SqlClientFactory.Instance); - DbProviderFactories.RegisterFactory("System.Data.SQLite", () => System.Data.SQLite.SQLiteFactory.Instance); - } - - [Test] - [Category("MySql")] - public void CanLoad_MySqlProvider() - { - var configReader = new ConfigurationReader(); - var connectionString = configReader.GetDatabaseConnectionConfigById(DatabaseConnectionConfigIds.MySQLId)?.ConnectionString; - if (!String.IsNullOrEmpty(connectionString)) - { - using var provider = ProviderFactory.Create(ProviderTypes.Mysql, connectionString, null); - Assert.That(provider, Is.Not.Null); - } - } - - [Test] - [Category("Oracle")] - public void CanLoad_OracleProvider() - { - var configReader = new ConfigurationReader(); - var connectionString = configReader.GetDatabaseConnectionConfigById(DatabaseConnectionConfigIds.OracleId)?.ConnectionString; - if (!String.IsNullOrEmpty(connectionString)) - { - using var provider = ProviderFactory.Create(ProviderTypes.Oracle, connectionString, null); - Assert.That(provider, Is.Not.Null); - } - } - - [Test] - [Category("Postgre")] - public void CanLoad_PostgreSQLProvider() - { - var configReader = new ConfigurationReader(); - var connectionString = configReader.GetDatabaseConnectionConfigById(DatabaseConnectionConfigIds.PostgreSQL)?.ConnectionString; - if (!String.IsNullOrEmpty(connectionString)) - { - using var provider = ProviderFactory.Create(ProviderTypes.PostgreSQL, connectionString, null); - Assert.That(provider, Is.Not.Null); - } - } - - [Test] - [Category("SQLite")] - public void CanLoad_SQLiteProvider() - { - var configReader = new ConfigurationReader(); - var connectionString = configReader.GetDatabaseConnectionConfigById(DatabaseConnectionConfigIds.SQLiteId)?.ConnectionString; - if (!String.IsNullOrEmpty(connectionString)) - { - using var provider = ProviderFactory.Create(ProviderTypes.SQLite, connectionString, null); - Assert.That(provider, Is.Not.Null); - } - } - - [Test] - [Category("SqlServer")] - public void CanLoad_SqlServerProvider() - { - var configReader = new ConfigurationReader(); - var connectionString = configReader.GetDatabaseConnectionConfigById(DatabaseConnectionConfigIds.SQLServerId)?.ConnectionString; - if (!String.IsNullOrEmpty(connectionString)) - { - using var provider = ProviderFactory.Create(ProviderTypes.SqlServer, connectionString, null); - Assert.That(provider, Is.Not.Null); - } - } -} +using System; +using System.Configuration; +using System.Diagnostics; +using System.Linq; +using DotNetProjects.Migrator; +using DotNetProjects.Migrator.Providers; +using Migrator.Tests.Settings; +using Migrator.Tests.Settings.Config; +using Npgsql; +using NUnit.Framework; + +namespace Migrator.Tests; + +[TestFixture] +public class ProviderFactoryTest +{ + [Test] + public void CanGetDialectsForProvider() + { + foreach (var provider in Enum.GetValues(typeof(ProviderTypes)).Cast().Where(x => x != ProviderTypes.none)) + { + Assert.That(ProviderFactory.DialectForProvider(provider), Is.Not.Null); + } + + Assert.That(ProviderFactory.DialectForProvider(ProviderTypes.none), Is.Null); + } + + [SetUp] + public void SetUp() + { + DbProviderFactories.RegisterFactory("Npgsql", () => NpgsqlFactory.Instance); + DbProviderFactories.RegisterFactory("MySql.Data.MySqlClient", () => MySql.Data.MySqlClient.MySqlClientFactory.Instance); + DbProviderFactories.RegisterFactory("Oracle.DataAccess.Client", () => Oracle.ManagedDataAccess.Client.OracleClientFactory.Instance); + DbProviderFactories.RegisterFactory("System.Data.SqlClient", () => Microsoft.Data.SqlClient.SqlClientFactory.Instance); + DbProviderFactories.RegisterFactory("System.Data.SQLite", () => System.Data.SQLite.SQLiteFactory.Instance); + } + + [Test] + [Category("MySql")] + public void CanLoad_MySqlProvider() + { + var configReader = new ConfigurationReader(); + var connectionString = configReader.GetDatabaseConnectionConfigById(DatabaseConnectionConfigIds.MySQLId)?.ConnectionString; + + using var provider = ProviderFactory.Create(ProviderTypes.Mysql, connectionString, null); + Assert.That(provider, Is.Not.Null); + } + + [Test] + [Category("Oracle")] + public void CanLoad_OracleProvider() + { + var configReader = new ConfigurationReader(); + var connectionString = configReader.GetDatabaseConnectionConfigById(DatabaseConnectionConfigIds.OracleId)?.ConnectionString; + + using var provider = ProviderFactory.Create(ProviderTypes.Oracle, connectionString, null); + Assert.That(provider, Is.Not.Null); + } + + [Test] + [Category("Postgre")] + public void CanLoad_PostgreSQLProvider() + { + var configReader = new ConfigurationReader(); + var connectionString = configReader.GetDatabaseConnectionConfigById(DatabaseConnectionConfigIds.PostgreSQL)?.ConnectionString; + + using var provider = ProviderFactory.Create(ProviderTypes.PostgreSQL, connectionString, null); + Assert.That(provider, Is.Not.Null); + } + + [Test] + [Category("SQLite")] + public void CanLoad_SQLiteProvider() + { + var configReader = new ConfigurationReader(); + var connectionString = configReader.GetDatabaseConnectionConfigById(DatabaseConnectionConfigIds.SQLiteId)?.ConnectionString; + + using var provider = ProviderFactory.Create(ProviderTypes.SQLite, connectionString, null); + Assert.That(provider, Is.Not.Null); + } + + [Test] + [Category("SqlServer")] + public void CanLoad_SqlServerProvider() + { + var configReader = new ConfigurationReader(); + var connectionString = configReader.GetDatabaseConnectionConfigById(DatabaseConnectionConfigIds.SQLServerId)?.ConnectionString; + + using var provider = ProviderFactory.Create(ProviderTypes.SqlServer, connectionString, null); + Assert.That(provider, Is.Not.Null); + } +} From ef904bad35b810893f0f7dfb5ce3d6de3f53b6c7 Mon Sep 17 00:00:00 2001 From: JaBistDuNarrisch Date: Tue, 30 Sep 2025 21:56:19 +0200 Subject: [PATCH 7/7] Update --- ...ostgreSqlDatabaseIntegrationTestService.cs | 226 ++--- .../Base/TransformationProviderBase.cs | 334 +++--- .../TransformationProviderGenericMiscTests.cs | 947 +++++++++--------- src/Migrator.Tests/appsettings.json | 2 +- 4 files changed, 751 insertions(+), 758 deletions(-) diff --git a/src/Migrator.Tests/Database/DerivedDatabaseIntegrationTestServices/PostgreSqlDatabaseIntegrationTestService.cs b/src/Migrator.Tests/Database/DerivedDatabaseIntegrationTestServices/PostgreSqlDatabaseIntegrationTestService.cs index f8265016..14e18f76 100644 --- a/src/Migrator.Tests/Database/DerivedDatabaseIntegrationTestServices/PostgreSqlDatabaseIntegrationTestService.cs +++ b/src/Migrator.Tests/Database/DerivedDatabaseIntegrationTestServices/PostgreSqlDatabaseIntegrationTestService.cs @@ -1,113 +1,115 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using LinqToDB; -using LinqToDB.Async; -using LinqToDB.Data; -using Mapster; -using Migrator.Tests.Database.DatabaseName.Interfaces; -using Migrator.Tests.Database.Interfaces; -using Migrator.Tests.Database.Models; -using Migrator.Tests.Settings.Models; -using Npgsql; - -namespace Migrator.Tests.Database.DerivedDatabaseIntegrationTestServices; - -public class PostgreSqlDatabaseIntegrationTestService(TimeProvider timeProvider, IDatabaseNameService databaseNameService) - : DatabaseIntegrationTestServiceBase(databaseNameService), IDatabaseIntegrationTestService -{ - public override async Task CreateTestDatabaseAsync(DatabaseConnectionConfig databaseConnectionConfig, CancellationToken cancellationToken) - { - var clonedDatabaseConnectionConfig = databaseConnectionConfig.Adapt(); - - var builder = new NpgsqlConnectionStringBuilder - { - ConnectionString = clonedDatabaseConnectionConfig.ConnectionString, - Database = "postgres" - }; - - List databaseNames; - - using (var context = new DataConnection(new DataOptions().UsePostgreSQL(builder.ConnectionString))) - { - databaseNames = await context.FromSql("SELECT datname from pg_database WHERE datistemplate = false").ToListAsync(cancellationToken); - } - - var toBeDeletedDatabaseNames = databaseNames.Where(x => - { - var creationDate = DatabaseNameService.ReadTimeStampFromString(x); - - return creationDate.HasValue && creationDate.Value < timeProvider.GetUtcNow().Subtract(MinTimeSpanBeforeDatabaseDeletion); - }).ToList(); - - foreach (var databaseName in toBeDeletedDatabaseNames) - { - var databaseInfoToBeDeleted = new DatabaseInfo { DatabaseConnectionConfig = databaseConnectionConfig, DatabaseName = databaseName }; - await DropDatabaseAsync(databaseInfoToBeDeleted, cancellationToken); - } - - var newDatabaseName = DatabaseNameService.CreateDatabaseName(); - using (var context = new DataConnection(new DataOptions().UsePostgreSQL(builder.ConnectionString))) - { - await context.ExecuteAsync($"CREATE DATABASE \"{newDatabaseName}\"", cancellationToken); - } - - var connectionStringBuilder2 = new NpgsqlConnectionStringBuilder(clonedDatabaseConnectionConfig.ConnectionString) - { - Database = newDatabaseName - }; - - clonedDatabaseConnectionConfig.ConnectionString = connectionStringBuilder2.ConnectionString; - - var databaseInfo = new DatabaseInfo - { - DatabaseConnectionConfig = clonedDatabaseConnectionConfig, - DatabaseName = newDatabaseName - }; - - return databaseInfo; - } - - public override async Task DropDatabaseAsync(DatabaseInfo databaseInfo, CancellationToken cancellationToken) - { - var creationDate = DatabaseNameService.ReadTimeStampFromString(databaseInfo.DatabaseName); - - if (!creationDate.HasValue) - { - throw new Exception("You tried to drop a database that was not created by this service. For safety reasons we deny your request."); - } - - var builder = new NpgsqlConnectionStringBuilder(databaseInfo.DatabaseConnectionConfig.ConnectionString) - { - Database = "postgres" - }; - - var dataOptions = new DataOptions().UsePostgreSQL(builder.ConnectionString); - - using var context = new DataConnection(dataOptions); - - try - { - await context.ExecuteAsync($"SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = '{databaseInfo.DatabaseName}'", cancellationToken); - await context.ExecuteAsync($"DROP DATABASE \"{databaseInfo.DatabaseName}\"", cancellationToken); - } - catch - { - await Task.Delay(2000, cancellationToken); - - var count = await context.ExecuteAsync($"SELECT COUNT(*) from pg_database WHERE datistemplate = false AND datname = '{databaseInfo.DatabaseName}'", cancellationToken); - - if (count == 1) - { - throw; - } - else - { - // The database was removed by another asynchronously running test that kicked in earlier. - // That's ok for us as we have achieved our objective. - } - } - } +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using LinqToDB; +using LinqToDB.Async; +using LinqToDB.Data; +using Mapster; +using Migrator.Tests.Database.DatabaseName.Interfaces; +using Migrator.Tests.Database.Interfaces; +using Migrator.Tests.Database.Models; +using Migrator.Tests.Settings.Models; +using Npgsql; + +namespace Migrator.Tests.Database.DerivedDatabaseIntegrationTestServices; + +public class PostgreSqlDatabaseIntegrationTestService(TimeProvider timeProvider, IDatabaseNameService databaseNameService) + : DatabaseIntegrationTestServiceBase(databaseNameService), IDatabaseIntegrationTestService +{ + public override async Task CreateTestDatabaseAsync(DatabaseConnectionConfig databaseConnectionConfig, CancellationToken cancellationToken) + { + var clonedDatabaseConnectionConfig = databaseConnectionConfig.Adapt(); + + var builder = new NpgsqlConnectionStringBuilder + { + ConnectionString = clonedDatabaseConnectionConfig.ConnectionString, + Database = "postgres" + }; + + List databaseNames; + + using (var context = new DataConnection(new DataOptions().UsePostgreSQL(builder.ConnectionString))) + { + databaseNames = await context.QueryToListAsync("SELECT datname from pg_database WHERE datistemplate = false", cancellationToken); + } + + var toBeDeletedDatabaseNames = databaseNames.Where(x => + { + var creationDate = DatabaseNameService.ReadTimeStampFromString(x); + + return creationDate.HasValue && creationDate.Value < timeProvider.GetUtcNow().Subtract(MinTimeSpanBeforeDatabaseDeletion); + }).ToList(); + + foreach (var databaseName in toBeDeletedDatabaseNames) + { + var databaseInfoToBeDeleted = new DatabaseInfo { DatabaseConnectionConfig = databaseConnectionConfig, DatabaseName = databaseName }; + await DropDatabaseAsync(databaseInfoToBeDeleted, cancellationToken); + } + + var newDatabaseName = DatabaseNameService.CreateDatabaseName(); + using (var context = new DataConnection(new DataOptions().UsePostgreSQL(builder.ConnectionString))) + { + await context.ExecuteAsync($"CREATE DATABASE \"{newDatabaseName}\"", cancellationToken); + } + + var connectionStringBuilder2 = new NpgsqlConnectionStringBuilder(clonedDatabaseConnectionConfig.ConnectionString) + { + Database = newDatabaseName + }; + + clonedDatabaseConnectionConfig.ConnectionString = connectionStringBuilder2.ConnectionString; + + var databaseInfo = new DatabaseInfo + { + DatabaseConnectionConfig = clonedDatabaseConnectionConfig, + DatabaseName = newDatabaseName + }; + + return databaseInfo; + } + + public override async Task DropDatabaseAsync(DatabaseInfo databaseInfo, CancellationToken cancellationToken) + { + var creationDate = DatabaseNameService.ReadTimeStampFromString(databaseInfo.DatabaseName); + + if (!creationDate.HasValue) + { + throw new Exception("You tried to drop a database that was not created by this service. For safety reasons we deny your request."); + } + + var builder = new NpgsqlConnectionStringBuilder(databaseInfo.DatabaseConnectionConfig.ConnectionString) + { + Database = "postgres" + }; + + var dataOptions = new DataOptions().UsePostgreSQL(builder.ConnectionString); + + using (var context = new DataConnection(dataOptions)) + { + + try + { + await context.ExecuteAsync($"SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = '{databaseInfo.DatabaseName}'", cancellationToken); + await context.ExecuteAsync($"DROP DATABASE \"{databaseInfo.DatabaseName}\"", cancellationToken); + } + catch + { + await Task.Delay(2000, cancellationToken); + + var count = await context.ExecuteAsync($"SELECT COUNT(*) from pg_database WHERE datistemplate = false AND datname = '{databaseInfo.DatabaseName}'", cancellationToken); + + if (count == 1) + { + throw; + } + else + { + // The database was removed by another asynchronously running test that kicked in earlier. + // That's ok for us as we have achieved our objective. + } + } + } + } } \ No newline at end of file diff --git a/src/Migrator.Tests/Providers/Base/TransformationProviderBase.cs b/src/Migrator.Tests/Providers/Base/TransformationProviderBase.cs index eae08dea..dfd236c2 100644 --- a/src/Migrator.Tests/Providers/Base/TransformationProviderBase.cs +++ b/src/Migrator.Tests/Providers/Base/TransformationProviderBase.cs @@ -1,171 +1,163 @@ -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 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 Npgsql; -using NUnit.Framework; - -namespace Migrator.Tests.Providers.Base; - -/// -/// Base class for provider tests. -/// -public abstract class TransformationProviderBase -{ - private IDbConnection _dbConnection; - - [TearDown] - public virtual void TearDown() - { - DropTestTables(); - - Provider?.Rollback(); - - if (_dbConnection != null) - { - if (_dbConnection.State == ConnectionState.Open) - { - _dbConnection.Close(); - } - - _dbConnection.Dispose(); - } - } - - protected void DropTestTables() - { - // Because MySql doesn't support schema transaction - // we got to remove the tables manually... sad... - try - { - Provider.RemoveTable("TestTwo"); - } - catch (Exception) - { - } - try - { - Provider.RemoveTable("Test"); - } - catch (Exception) - { - } - try - { - Provider.RemoveTable("SchemaInfo"); - } - catch (Exception) - { - } - } - - protected ITransformationProvider Provider; - - protected async Task BeginOracleTransactionAsync() - { - 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(); - } - - protected async Task BeginPostgreSQLTransactionAsync() - { - using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10)); - var configReader = new ConfigurationReader(); - - var databaseConnectionConfig = configReader.GetDatabaseConnectionConfigById(DatabaseConnectionConfigIds.PostgreSQL); - - var connectionString = databaseConnectionConfig?.ConnectionString; - - if (string.IsNullOrEmpty(connectionString)) - { - throw new IgnoreException("No Postgre SQL connection string is set."); - } - - DbProviderFactories.RegisterFactory("Npgsql", () => Npgsql.NpgsqlFactory.Instance); - - 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); - - - _dbConnection = new NpgsqlConnection(databaseInfo.DatabaseConnectionConfig.ConnectionString); - - Provider = new PostgreSQLTransformationProvider(new PostgreSQLDialect(), _dbConnection, null, "default", "Npgsql"); - Provider.BeginTransaction(); - - await Task.CompletedTask; - } - - protected async Task BeginSQLiteTransactionAsync() - { - var configReader = new ConfigurationReader(); - var connectionString = configReader.GetDatabaseConnectionConfigById(DatabaseConnectionConfigIds.SQLiteId) - .ConnectionString; - - Provider = new SQLiteTransformationProvider(new SQLiteDialect(), connectionString, "default", null); - Provider.BeginTransaction(); - - await Task.CompletedTask; - } - - protected async Task BeginSQLServerTransactionAsync() - { - var configReader = new ConfigurationReader(); - - var databaseConnectionConfig = configReader.GetDatabaseConnectionConfigById(DatabaseConnectionConfigIds.SQLServerId); - - var connectionString = databaseConnectionConfig?.ConnectionString; - - if (string.IsNullOrEmpty(connectionString)) - { - throw new IgnoreException($"No SQL Server {nameof(DatabaseConnectionConfig.ConnectionString)} is set."); - } - - DbProviderFactories.RegisterFactory("Microsoft.Data.SqlClient", () => Microsoft.Data.SqlClient.SqlClientFactory.Instance); - - using var container = new Container(); - container.RegisterDatabaseIntegrationTestService(); - var databaseIntegrationTestServiceFactory = container.Resolve(); - var sqlServerIntegrationTestService = databaseIntegrationTestServiceFactory.Create(DatabaseProviderType.SQLServer); - var databaseInfo = await sqlServerIntegrationTestService.CreateTestDatabaseAsync(databaseConnectionConfig, CancellationToken.None); - - Provider = new SqlServerTransformationProvider(new SqlServerDialect(), databaseInfo.DatabaseConnectionConfig.ConnectionString, "dbo", "default", "Microsoft.Data.SqlClient"); - - Provider.BeginTransaction(); - } -} +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 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 Npgsql; +using NUnit.Framework; + +namespace Migrator.Tests.Providers.Base; + +/// +/// Base class for provider tests. +/// +public abstract class TransformationProviderBase +{ + private IDbConnection _dbConnection; + protected ITransformationProvider Provider; + + [TearDown] + public virtual void TearDown() + { + DropTestTables(); + + Provider?.Rollback(); + + _dbConnection?.Dispose(); + } + + protected void DropTestTables() + { + // Because MySql doesn't support schema transaction + // we got to remove the tables manually... sad... + try + { + Provider.RemoveTable("TestTwo"); + } + catch (Exception) + { + } + try + { + Provider.RemoveTable("Test"); + } + catch (Exception) + { + } + try + { + Provider.RemoveTable("SchemaInfo"); + } + catch (Exception) + { + } + } + + + protected async Task BeginOracleTransactionAsync() + { + 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(); + } + + protected async Task BeginPostgreSQLTransactionAsync() + { + using var cts = new CancellationTokenSource(TimeSpan.FromMinutes(1)); + var configReader = new ConfigurationReader(); + + var databaseConnectionConfig = configReader.GetDatabaseConnectionConfigById(DatabaseConnectionConfigIds.PostgreSQL); + + var connectionString = databaseConnectionConfig?.ConnectionString; + + if (string.IsNullOrEmpty(connectionString)) + { + throw new IgnoreException("No Postgre SQL connection string is set."); + } + + DbProviderFactories.RegisterFactory("Npgsql", () => Npgsql.NpgsqlFactory.Instance); + + 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); + + + _dbConnection = new NpgsqlConnection(databaseInfo.DatabaseConnectionConfig.ConnectionString); + + Provider = new PostgreSQLTransformationProvider(new PostgreSQLDialect(), _dbConnection, null, "default", "Npgsql"); + Provider.BeginTransaction(); + + await Task.CompletedTask; + } + + protected async Task BeginSQLiteTransactionAsync() + { + var configReader = new ConfigurationReader(); + var connectionString = configReader.GetDatabaseConnectionConfigById(DatabaseConnectionConfigIds.SQLiteId) + .ConnectionString; + + Provider = new SQLiteTransformationProvider(new SQLiteDialect(), connectionString, "default", null); + Provider.BeginTransaction(); + + await Task.CompletedTask; + } + + protected async Task BeginSQLServerTransactionAsync() + { + var configReader = new ConfigurationReader(); + + var databaseConnectionConfig = configReader.GetDatabaseConnectionConfigById(DatabaseConnectionConfigIds.SQLServerId); + + var connectionString = databaseConnectionConfig?.ConnectionString; + + if (string.IsNullOrEmpty(connectionString)) + { + throw new IgnoreException($"No SQL Server {nameof(DatabaseConnectionConfig.ConnectionString)} is set."); + } + + DbProviderFactories.RegisterFactory("Microsoft.Data.SqlClient", () => Microsoft.Data.SqlClient.SqlClientFactory.Instance); + + using var container = new Container(); + container.RegisterDatabaseIntegrationTestService(); + var databaseIntegrationTestServiceFactory = container.Resolve(); + var sqlServerIntegrationTestService = databaseIntegrationTestServiceFactory.Create(DatabaseProviderType.SQLServer); + var databaseInfo = await sqlServerIntegrationTestService.CreateTestDatabaseAsync(databaseConnectionConfig, CancellationToken.None); + + Provider = new SqlServerTransformationProvider(new SqlServerDialect(), databaseInfo.DatabaseConnectionConfig.ConnectionString, "dbo", "default", "Microsoft.Data.SqlClient"); + + Provider.BeginTransaction(); + } +} diff --git a/src/Migrator.Tests/Providers/Generic/TransformationProviderGenericMiscTests.cs b/src/Migrator.Tests/Providers/Generic/TransformationProviderGenericMiscTests.cs index 65971c7e..62249ae1 100644 --- a/src/Migrator.Tests/Providers/Generic/TransformationProviderGenericMiscTests.cs +++ b/src/Migrator.Tests/Providers/Generic/TransformationProviderGenericMiscTests.cs @@ -1,474 +1,473 @@ -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; - } -} +using System; +using System.Data; +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/appsettings.json b/src/Migrator.Tests/appsettings.json index a5e74631..b2173754 100644 --- a/src/Migrator.Tests/appsettings.json +++ b/src/Migrator.Tests/appsettings.json @@ -10,7 +10,7 @@ }, { "Id": "PostgreSQL", - "ConnectionString": "Server=localhost;Port=5432;Database=postgres;User Id=testuser;Password=testpass;Pooling=true;" + "ConnectionString": "Server=localhost;Port=5432;Database=postgres;User Id=testuser;Password=testpass;Pooling=false;" }, { "Id": "Oracle",