Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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<UpdateFromTableToTableModel> 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<UpdateFromTableToTableModel> 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<UpdateFromTableToTableModel>((x, y) =>
x.Column1 == y.Column1 &&
x.Column2 == y.Column2 &&
x.Column3 == y.Column3 &&
x.Column4 == y.Column4 &&
x.Column5 == y.Column5));
}
}
Original file line number Diff line number Diff line change
@@ -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; }
}
Original file line number Diff line number Diff line change
@@ -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));
}
}
Original file line number Diff line number Diff line change
@@ -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();
}
}
Original file line number Diff line number Diff line change
@@ -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();
}
}
Original file line number Diff line number Diff line change
@@ -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();
}
}
Original file line number Diff line number Diff line change
@@ -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();
}
}
11 changes: 11 additions & 0 deletions src/Migrator/Framework/ITransformationProvider.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Data;
using DotNetProjects.Migrator.Framework.Models;

namespace DotNetProjects.Migrator.Framework;

Expand Down Expand Up @@ -518,6 +519,7 @@ public interface ITransformationProvider : IDisposable
/// Removes PK, FKs, Unique and CHECK constraints.
/// </summary>
/// <param name="table"></param>
[Obsolete("Drop all constraints separately.")]
void RemoveAllConstraints(string table);

/// <summary>
Expand Down Expand Up @@ -644,6 +646,15 @@ IDataReader SelectComplex(IDbCommand cmd, string table, string[] columns, string

int Update(string table, string[] columns, object[] values, string[] whereColumns, object[] whereValues);

/// <summary>
/// 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 <paramref name="conditionColumnPairs"/>
/// </summary>
/// <param name="tableSourceNotQuoted"></param>
/// <param name="tableTargetNotQuoted"></param>
/// <param name="fromSourceToTargetColumnPairs">Pairs that represent the name of the source column and the column in the target table to be updated.</param>
/// <param name="conditionColumnPairs">Pairs that represent the name of the source column and the name of the target tabel used to match the rows.</param>
void UpdateFromTableToTable(string tableSourceNotQuoted, string tableTargetNotQuoted, ColumnPair[] fromSourceToTargetColumnPairs, ColumnPair[] conditionColumnPairs);

/// <summary>
/// Get a command instance
/// </summary>
Expand Down
47 changes: 47 additions & 0 deletions src/Migrator/Providers/Impl/Oracle/OracleTransformationProvider.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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 conditionStrings = conditionColumnPairs.Select(x => $"t.{QuoteColumnNameIfRequired(x.ColumnNameTargetNotQuoted)} = s.{QuoteColumnNameIfRequired(x.ColumnNameSourceNotQuoted)}");

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 USING {tableNameSource} s ON ({conditionStringsJoined}) WHEN MATCHED THEN UPDATE SET {assignStringsJoined}";
ExecuteNonQuery(sql);
}

private string SchemaInfoTableName
{
get
Expand Down
Loading
Loading