Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
3 changes: 2 additions & 1 deletion docs/fluent-operation-coverage.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"AddTable": "Create.Table(...).WithFields(...) / WithEngine(...)",
"AddColumn": "Create.Column(name).OnTable(table).OfType(...) / Create.Column(definition).OnTable(table)",
"AddColumn": "Create.Column(name).OnTable(table).OfType(...) / Create.Column(definition).OnTable(table); Database.AddColumn(table, column, primaryKey) for the combined operation",
"RemoveUniqueConstraint": "Database.RemoveUniqueConstraint(table, definition) for exact metadata-based selection, including unnamed SQLite constraints",
"ChangeColumn": "Alter.Column(name).OnTable(table).OfType(...) / Alter.Column(definition).OnTable(table)",
"AddPrimaryKey": "Create.PrimaryKey(name).OnTable(table).WithColumns(...)",
"AddPrimaryKeyNonClustered": "Create.NonClusteredPrimaryKey(name).OnTable(table).WithColumns(...)",
Expand Down
25 changes: 12 additions & 13 deletions docs/migration-guide-12.1-to-13.md
Original file line number Diff line number Diff line change
Expand Up @@ -261,24 +261,23 @@ default remains text; use a CLR `Guid` when authoring a GUID default.

### SQLite identity columns

SQLite requires the identity column and its single-column primary key in the
same table definition. Separate `AddColumn` and `AddPrimaryKey` calls create an
invalid intermediate definition. Use the SQLite provider's atomic rebuild API:
Use the provider-independent overload to add a column with an explicit primary key:

```csharp
var sqlite = (SQLiteTransformationProvider)Database;
var definition = sqlite.GetSQLiteTableInfo("Settings");
definition.Columns.Add(new Column("Id", DbType.Int32) { IsIdentity = true });
definition.ColumnMappings.Add(new MappingInfo { OldName = null, NewName = "Id" });
definition.PrimaryKey = new PrimaryKeyConstraint("PK_Settings", "Id");
sqlite.RecreateTable(definition);
Database.AddColumn("Settings",
new Column("Id", DbType.Int32) { IsIdentity = true },
new PrimaryKeyConstraint("PK_Settings", "Id"));
```

`SQLiteTransformationProvider` is in `DotNetProjects.Migrator.Providers.Impl.SQLite`;
`MappingInfo` is in its `Models` namespace. Existing rows receive generated IDs.
This example assumes the table has no existing primary key or dependent foreign
keys requiring a separate migration plan.
The key must have a nonempty name and valid, ordered column members. Existing primary keys are rejected rather than replaced, and caller-owned definitions are not mutated. SQLite adds both definitions in a single transactional table rebuild, preserving existing rows and generating IDs. MySQL/MariaDB add both in one ALTER statement because AUTO_INCREMENT must be indexed immediately. Other providers use their normal AddColumn/AddPrimaryKey operations and DDL transaction semantics; the overload does not promise cross-provider rollback if a later DDL statement fails. Existing columns in a composite key must already meet the provider's requirements.

`IsIdentity` alone still does not imply a primary key. Separate AddColumn/AddPrimaryKey calls cannot introduce an SQLite identity column. SQLite downgrade can use RemovePrimaryKey followed by RemoveColumn; the migration runner manages SQLite's foreign-key state.

### Removing legacy unnamed unique constraints

Use `Database.RemoveUniqueConstraint(table, constraint)` with a `UniqueConstraint` returned by `GetTableConstraints`. The operation matches both the declared name and ordered columns, and requires exactly one match. SQLite rebuilds internally to remove unnamed legacy constraints without discarding other unique/check constraints. Other providers remove the verified named constraint using their existing DDL implementation. Unknown or ambiguous selections fail before mutation.

Custom ITransformationProvider implementations must implement these two new methods; implementations derived from TransformationProvider inherit the portable defaults. NoOpTransformationProvider supports both as no-ops. These are direct provider APIs; fluent callers can use Database for these combined operations.
## Identifier quoting and renamed tables

Use `QuoteColumnNameIfRequired` for columns in authored SQL and
Expand Down
53 changes: 53 additions & 0 deletions src/Migrator.Tests/ColumnWithPrimaryKeySqlTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using System.Collections.Generic;
using System.Data;
using DotNetProjects.Migrator.Framework;
using DotNetProjects.Migrator.Providers;
using DotNetProjects.Migrator.Providers.Impl.Mysql;
using DotNetProjects.Migrator.Providers.Impl.SqlServer;
using NUnit.Framework;

namespace Migrator.Tests;

[Category("Unit")]
public class ColumnWithPrimaryKeySqlTests
{
private sealed class RecordingMySqlProvider() : MySqlTransformationProvider(new MysqlDialect(), (IDbConnection)null, null, null)
{
public List<string> Statements { get; } = [];
public override bool TableExists(string table) => true;
public override Column[] GetColumns(string table) => [new Column("Value", DbType.String)];
public override TableConstraint[] GetTableConstraints(string table) => [];
public override int ExecuteNonQuery(string sql) { Statements.Add(sql); return 0; }
}

private sealed class RecordingSqlServerProvider() : SqlServerTransformationProvider(new SqlServerDialect(), (IDbConnection)null, null, null, null)
{
public List<string> Statements { get; } = [];
public override bool TableExists(string table) => true;
public override Column[] GetColumns(string table) => [new Column("Value", DbType.String)];
public override TableConstraint[] GetTableConstraints(string table) => [];
public override int ExecuteNonQuery(string sql) { Statements.Add(sql); return 0; }
}

[Test]
public void MySqlAddsAutoIncrementAndKeyInOneStatement()
{
using var provider = new RecordingMySqlProvider();
provider.AddColumn("Settings", new Column("Id", DbType.Int32) { IsIdentity = true }, new PrimaryKeyConstraint("PK_Settings", "Id"));
Assert.That(provider.Statements, Has.Count.EqualTo(1));
Assert.That(provider.Statements[0], Does.Contain("AUTO_INCREMENT").IgnoreCase.And.Contain("PRIMARY KEY").IgnoreCase);
Assert.That(provider.Statements[0], Does.Contain(", ADD CONSTRAINT"));
}

[Test]
public void SqlServerUsesItsNonclusteredPrimaryKeyImplementation()
{
using var provider = new RecordingSqlServerProvider();
var column = new Column("Id", DbType.Int32) { IsIdentity = true };
provider.AddColumn("Settings", column, new PrimaryKeyConstraint("PK_Settings", "Id") { NonClustered = true });
Assert.That(provider.Statements, Has.Count.EqualTo(2));
Assert.That(provider.Statements[0], Does.Contain("IDENTITY").IgnoreCase);
Assert.That(provider.Statements[1], Does.Contain("PRIMARY KEY NONCLUSTERED").IgnoreCase);
Assert.That(column.IsNullable, Is.True);
}
}
104 changes: 104 additions & 0 deletions src/Migrator.Tests/ColumnWithPrimaryKeyTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
using System;
using System.Data;
using System.Linq;
using DotNetProjects.Migrator;
using DotNetProjects.Migrator.Framework;
using DotNetProjects.Migrator.Providers;
using NUnit.Framework;
using UniqueConstraint = DotNetProjects.Migrator.Framework.UniqueConstraint;

namespace Migrator.Tests;

[Category("SQLite")]
[TestFixture(false)]
[TestFixture(true)]
public class ColumnWithPrimaryKeyTests(bool systemData)
{
private IDbConnection connection;
private ITransformationProvider provider;

[SetUp]
public void SetUp()
{
connection = systemData
? new System.Data.SQLite.SQLiteConnection("Data Source=:memory:")
: new Microsoft.Data.Sqlite.SqliteConnection("Data Source=:memory:;Foreign Keys=False");
connection.Open();
provider = ProviderFactory.Create(ProviderTypes.SQLite, connection, null);
provider.ExecuteNonQuery("CREATE TABLE Settings (Value TEXT, CONSTRAINT UQ_Value UNIQUE(Value), CHECK(length(Value)>0)); CREATE INDEX IX_Value ON Settings(Value); INSERT INTO Settings VALUES ('first'), ('second');");
}

[TearDown]
public void TearDown() { provider.Dispose(); connection.Dispose(); }

[Test]
public void AddsIdentityAndKeyTogetherAndSupportsPortableDowngrade()
{
var column = new Column("Id", DbType.Int32) { IsIdentity = true };
provider.AddColumn("Settings", column, new PrimaryKeyConstraint("PK_Settings", "Id"));
Assert.That(column.IsNullable, Is.True, "The caller's definition must not be mutated.");
Assert.That(provider.ExecuteScalar("SELECT COUNT(DISTINCT Id) FROM Settings"), Is.EqualTo(2));
Assert.That(provider.GetTableConstraints("Settings").OfType<PrimaryKeyConstraint>().Single().Name, Is.EqualTo("PK_Settings"));
provider.Insert("Settings", ["Value"], ["third"]);
Assert.That(provider.ExecuteScalar("SELECT Id FROM Settings WHERE Value='third'"), Is.EqualTo(3));
provider.RemovePrimaryKey("Settings");
provider.RemoveColumn("Settings", "Id");
Assert.That(provider.GetColumns("Settings").Select(c => c.Name), Is.EqualTo(new[] { "Value" }));
Assert.That(provider.ExecuteScalar("SELECT COUNT(*) FROM Settings"), Is.EqualTo(3));
Assert.That(provider.GetTableConstraints("Settings").OfType<UniqueConstraint>().Single().Name, Is.EqualTo("UQ_Value"));
Assert.That(provider.GetTableConstraints("Settings").OfType<CheckConstraint>().Count(), Is.EqualTo(1));
Assert.That(provider.GetIndexes("Settings").Any(index => index.Name == "IX_Value"), Is.True);
}

[TestCase("Missing")]
[TestCase("Value")]
public void InvalidIdentityKeyLeavesOriginalSchemaAndData(string keyColumn)
{
Assert.Catch(() => provider.AddColumn("Settings", new Column("Id", DbType.Int32) { IsIdentity = true }, new PrimaryKeyConstraint("PK_Settings", keyColumn)));
Assert.That(provider.ColumnExists("Settings", "Id"), Is.False);
Assert.That(provider.ExecuteScalar("SELECT COUNT(*) FROM Settings"), Is.EqualTo(2));
Assert.That(provider.GetTables(), Is.EqualTo(new[] { "Settings" }));
}

[Test]
public void ExistingPrimaryKeyIsNotReplaced()
{
provider.AddPrimaryKey("PK_Old", "Settings", "Value");
Assert.Catch(() => provider.AddColumn("Settings", new Column("Id", DbType.Int32) { IsIdentity = true }, new PrimaryKeyConstraint("PK_New", "Id")));
Assert.That(provider.ColumnExists("Settings", "Id"), Is.False);
Assert.That(provider.GetTableConstraints("Settings").OfType<PrimaryKeyConstraint>().Single().Name, Is.EqualTo("PK_Old"));
}

[TestCase(false)]
[TestCase(true)]
public void RemovesOnlyTheSelectedUniqueDefinition(bool named)
{
provider.ExecuteNonQuery($"CREATE TABLE Legacy (A TEXT, B TEXT, {(named ? "CONSTRAINT UQ_A " : "")}UNIQUE(A), UNIQUE(B), CHECK(length(A)>0)); INSERT INTO Legacy VALUES ('a','b');");
var constraint = provider.GetTableConstraints("Legacy").OfType<UniqueConstraint>().Single(key => key.KeyColumns.SequenceEqual(new[] { "A" }));
provider.RemoveUniqueConstraint("Legacy", constraint);
Assert.That(provider.GetTableConstraints("Legacy").OfType<UniqueConstraint>().Single().KeyColumns, Is.EqualTo(new[] { "B" }));
Assert.That(provider.GetTableConstraints("Legacy").OfType<CheckConstraint>().Count(), Is.EqualTo(1));
provider.ExecuteNonQuery("INSERT INTO Legacy VALUES ('a','c')");
Assert.That(provider.ExecuteScalar("SELECT COUNT(*) FROM Legacy"), Is.EqualTo(2));
}

[Test]
public void CompositeKeyKeepsOrderAndFailedBackfillRollsBack()
{
provider.AddColumn("Settings", new Column("Part", DbType.Int32) { DefaultValue = 1 }, new PrimaryKeyConstraint("PK_Settings", "Part", "Value"));
Assert.That(provider.GetTableConstraints("Settings").OfType<PrimaryKeyConstraint>().Single().KeyColumns, Is.EqualTo(new[] { "Part", "Value" }));
provider.RemovePrimaryKey("Settings");
Assert.Catch(() => provider.AddColumn("Settings", new Column("Duplicate", DbType.String, 20) { DefaultValue = "same" }, new PrimaryKeyConstraint("PK_Duplicate", "Duplicate")));
Assert.That(provider.ColumnExists("Settings", "Duplicate"), Is.False);
Assert.That(provider.ExecuteScalar("SELECT COUNT(*) FROM Settings"), Is.EqualTo(2));
Assert.That(provider.GetTables(), Is.EqualTo(new[] { "Settings" }));
}

[Test]
public void UnknownUniqueDefinitionDoesNotRemoveOtherConstraints()
{
Assert.Throws<MigrationException>(() => provider.RemoveUniqueConstraint("Settings", new UniqueConstraint("UQ_Value", "Missing")));
Assert.That(provider.GetTableConstraints("Settings").OfType<UniqueConstraint>().Single().Name, Is.EqualTo("UQ_Value"));
Assert.That(provider.ExecuteScalar("SELECT COUNT(*) FROM Settings"), Is.EqualTo(2));
}
}
6 changes: 6 additions & 0 deletions src/Migrator/Framework/ITransformationProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,12 @@ public interface ITransformationProvider : IDisposable
/// <param name="table">The name of the table that will get the new column</param>
/// <param name="column">An instance of a <see cref="Column">Column</see> with the specified properties</param>
void AddColumn(string table, Column column);

/// <summary>Add a column and an explicit primary key as one schema operation. SQLite rebuilds once; other providers use their normal DDL transaction semantics.</summary>
void AddColumn(string table, Column column, PrimaryKeyConstraint primaryKey);

/// <summary>Remove the exact unique constraint returned by metadata, including unnamed SQLite constraints.</summary>
void RemoveUniqueConstraint(string table, UniqueConstraint constraint);

/// <summary>
/// Add a foreign key constraint
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@ namespace DotNetProjects.Migrator.Providers.Impl.Mysql;
/// </summary>
public class MySqlTransformationProvider : TransformationProvider
{
public override void AddColumn(string table, Column column, PrimaryKeyConstraint primaryKey)
{
var definition = PrepareColumnWithPrimaryKey(table, column, primaryKey);
// AUTO_INCREMENT must be indexed in the same ALTER statement, including on MariaDB.
ExecuteNonQuery($"ALTER TABLE {QuoteTableNameIfRequired(table)} ADD COLUMN {_dialect.GetAndMapColumnProperties(definition).ColumnSql}, ADD {_dialect.GetTableConstraintSql(primaryKey)}");
}

public MySqlTransformationProvider(Dialect dialect, string connectionString, string scope, string providerName)
: base(dialect, connectionString, null, scope) // we ignore schemas for MySql (schema == database for MySql)
{
Expand Down
30 changes: 30 additions & 0 deletions src/Migrator/Providers/Impl/SQLite/SQLiteTransformationProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -990,7 +990,7 @@
}

[Obsolete]
public override void AddTable(string table, string engine, string columns)

Check warning on line 993 in src/Migrator/Providers/Impl/SQLite/SQLiteTransformationProvider.cs

View workflow job for this annotation

GitHub Actions / Test (Unit)

Obsolete member 'SQLiteTransformationProvider.AddTable(string, string, string)' overrides non-obsolete member 'TransformationProvider.AddTable(string, string, string)'

Check warning on line 993 in src/Migrator/Providers/Impl/SQLite/SQLiteTransformationProvider.cs

View workflow job for this annotation

GitHub Actions / Test (SQLite)

Obsolete member 'SQLiteTransformationProvider.AddTable(string, string, string)' overrides non-obsolete member 'TransformationProvider.AddTable(string, string, string)'

Check warning on line 993 in src/Migrator/Providers/Impl/SQLite/SQLiteTransformationProvider.cs

View workflow job for this annotation

GitHub Actions / Test (MariaDB)

Obsolete member 'SQLiteTransformationProvider.AddTable(string, string, string)' overrides non-obsolete member 'TransformationProvider.AddTable(string, string, string)'

Check warning on line 993 in src/Migrator/Providers/Impl/SQLite/SQLiteTransformationProvider.cs

View workflow job for this annotation

GitHub Actions / Test (Firebird)

Obsolete member 'SQLiteTransformationProvider.AddTable(string, string, string)' overrides non-obsolete member 'TransformationProvider.AddTable(string, string, string)'

Check warning on line 993 in src/Migrator/Providers/Impl/SQLite/SQLiteTransformationProvider.cs

View workflow job for this annotation

GitHub Actions / Test (MySQL)

Obsolete member 'SQLiteTransformationProvider.AddTable(string, string, string)' overrides non-obsolete member 'TransformationProvider.AddTable(string, string, string)'

Check warning on line 993 in src/Migrator/Providers/Impl/SQLite/SQLiteTransformationProvider.cs

View workflow job for this annotation

GitHub Actions / Test (PostgreSQL)

Obsolete member 'SQLiteTransformationProvider.AddTable(string, string, string)' overrides non-obsolete member 'TransformationProvider.AddTable(string, string, string)'

Check warning on line 993 in src/Migrator/Providers/Impl/SQLite/SQLiteTransformationProvider.cs

View workflow job for this annotation

GitHub Actions / Test (Informix)

Obsolete member 'SQLiteTransformationProvider.AddTable(string, string, string)' overrides non-obsolete member 'TransformationProvider.AddTable(string, string, string)'

Check warning on line 993 in src/Migrator/Providers/Impl/SQLite/SQLiteTransformationProvider.cs

View workflow job for this annotation

GitHub Actions / Test (Sybase)

Obsolete member 'SQLiteTransformationProvider.AddTable(string, string, string)' overrides non-obsolete member 'TransformationProvider.AddTable(string, string, string)'

Check warning on line 993 in src/Migrator/Providers/Impl/SQLite/SQLiteTransformationProvider.cs

View workflow job for this annotation

GitHub Actions / Test (Db2)

Obsolete member 'SQLiteTransformationProvider.AddTable(string, string, string)' overrides non-obsolete member 'TransformationProvider.AddTable(string, string, string)'

Check warning on line 993 in src/Migrator/Providers/Impl/SQLite/SQLiteTransformationProvider.cs

View workflow job for this annotation

GitHub Actions / Test (Oracle)

Obsolete member 'SQLiteTransformationProvider.AddTable(string, string, string)' overrides non-obsolete member 'TransformationProvider.AddTable(string, string, string)'

Check warning on line 993 in src/Migrator/Providers/Impl/SQLite/SQLiteTransformationProvider.cs

View workflow job for this annotation

GitHub Actions / Test (Hana)

Obsolete member 'SQLiteTransformationProvider.AddTable(string, string, string)' overrides non-obsolete member 'TransformationProvider.AddTable(string, string, string)'

Check warning on line 993 in src/Migrator/Providers/Impl/SQLite/SQLiteTransformationProvider.cs

View workflow job for this annotation

GitHub Actions / Test (SQLServer)

Obsolete member 'SQLiteTransformationProvider.AddTable(string, string, string)' overrides non-obsolete member 'TransformationProvider.AddTable(string, string, string)'
{
throw new NotSupportedException();
}
Expand All @@ -1015,6 +1015,36 @@
RecreateTable(sqliteInfo);
}

public override void AddColumn(string table, Column column, PrimaryKeyConstraint primaryKey)
{
ArgumentException.ThrowIfNullOrWhiteSpace(table);
ArgumentNullException.ThrowIfNull(column);
ArgumentNullException.ThrowIfNull(primaryKey);
ArgumentException.ThrowIfNullOrWhiteSpace(primaryKey.Name);
var definition = GetSQLiteTableInfo(table);
if (definition.Columns.Any(existing => existing.Name.Equals(column.Name, StringComparison.OrdinalIgnoreCase)))
throw new MigrationException("Column already exists.");
if (definition.PrimaryKey != null) throw new MigrationException("The table already has a primary key.");
definition.Columns.Add(column.CopyDefinition());
ValidateKeyColumns(primaryKey.Name, primaryKey.KeyColumns, definition.Columns.ToArray());
definition.ColumnMappings.Add(new MappingInfo { NewName = column.Name });
definition.PrimaryKey = new PrimaryKeyConstraint(primaryKey.Name, primaryKey.KeyColumns) { NonClustered = primaryKey.NonClustered };
RecreateTable(definition);
}

public override void RemoveUniqueConstraint(string table, UniqueConstraint constraint)
{
ArgumentException.ThrowIfNullOrWhiteSpace(table);
ArgumentNullException.ThrowIfNull(constraint);
var definition = GetSQLiteTableInfo(table);
var matches = definition.Uniques.Where(candidate =>
string.Equals(candidate.Name, constraint.Name, StringComparison.OrdinalIgnoreCase) &&
candidate.KeyColumns.SequenceEqual(constraint.KeyColumns, StringComparer.OrdinalIgnoreCase)).ToArray();
if (matches.Length != 1) throw new MigrationException("Unique constraint selection must match exactly one definition.");
definition.Uniques.Remove(matches[0]);
RecreateTable(definition);
}

public override void AddColumn(string table, string columnName, DbType type, int size)
{
var column = new Column(columnName, type, size);
Expand Down
3 changes: 3 additions & 0 deletions src/Migrator/Providers/NoOpTransformationProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

using ForeignKeyConstraint = DotNetProjects.Migrator.Framework.ForeignKeyConstraint;
using Index = DotNetProjects.Migrator.Framework.Index;
using UniqueConstraint = DotNetProjects.Migrator.Framework.UniqueConstraint;

namespace DotNetProjects.Migrator.Providers;

Expand All @@ -15,6 +16,8 @@ namespace DotNetProjects.Migrator.Providers;
public class NoOpTransformationProvider : ITransformationProvider
{
public TableConstraint[] GetTableConstraints(string table) => [];
public void AddColumn(string table, Column column, PrimaryKeyConstraint primaryKey) { }
public void RemoveUniqueConstraint(string table, UniqueConstraint constraint) { }

public static readonly NoOpTransformationProvider Instance = new NoOpTransformationProvider();

Expand Down
38 changes: 38 additions & 0 deletions src/Migrator/Providers/TransformationProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1525,6 +1525,44 @@
AddColumn(table, _dialect.GetAndMapColumnProperties(column.CopyDefinition()).ColumnSql);
}

public virtual void AddColumn(string table, Column column, PrimaryKeyConstraint primaryKey)
{
var definition = PrepareColumnWithPrimaryKey(table, column, primaryKey);
AddColumn(table, definition);
if (primaryKey.NonClustered) AddPrimaryKeyNonClustered(primaryKey.Name, table, primaryKey.KeyColumns);
else AddPrimaryKey(primaryKey.Name, table, primaryKey.KeyColumns);
}

protected Column PrepareColumnWithPrimaryKey(string table, Column column, PrimaryKeyConstraint primaryKey)
{
ArgumentException.ThrowIfNullOrWhiteSpace(table);
ArgumentNullException.ThrowIfNull(column);
ArgumentNullException.ThrowIfNull(primaryKey);
ArgumentException.ThrowIfNullOrWhiteSpace(primaryKey.Name);
if (!TableExists(table)) throw new MigrationException("Table does not exist.");
var columns = GetColumns(table);
if (columns.Any(existing => existing.Name.Equals(column.Name, StringComparison.OrdinalIgnoreCase))) throw new MigrationException("Column already exists.");
if (GetTableConstraints(table).OfType<PrimaryKeyConstraint>().Any()) throw new MigrationException("The table already has a primary key.");
ValidateKeyColumns(primaryKey.Name, primaryKey.KeyColumns, columns.Append(column).ToArray());
// Validate provider-specific key options before any DDL.
_dialect.GetTableConstraintSql(primaryKey);
var definition = column.CopyDefinition();
if (primaryKey.KeyColumns.Contains(column.Name, StringComparer.OrdinalIgnoreCase)) definition.IsNullable = false;
return definition;
}

public virtual void RemoveUniqueConstraint(string table, UniqueConstraint constraint)
{
ArgumentException.ThrowIfNullOrWhiteSpace(table);
ArgumentNullException.ThrowIfNull(constraint);
var matches = GetTableConstraints(table).OfType<UniqueConstraint>().Where(candidate =>
string.Equals(candidate.Name, constraint.Name, StringComparison.OrdinalIgnoreCase) &&
candidate.KeyColumns.SequenceEqual(constraint.KeyColumns, StringComparer.OrdinalIgnoreCase)).ToArray();
if (matches.Length != 1) throw new MigrationException("Unique constraint selection must match exactly one definition.");
if (string.IsNullOrWhiteSpace(matches[0].Name)) throw new NotSupportedException("This provider cannot remove an unnamed unique constraint.");
RemoveConstraint(table, matches[0].Name);
}

public virtual void GenerateForeignKey(string primaryTable, string refTable)
{
GenerateForeignKey(primaryTable, refTable, ForeignKeyConstraintType.NoAction);
Expand Down Expand Up @@ -1756,7 +1794,7 @@
return GenerateParameterNameParameter(index);
}

protected virtual void ConfigureParameterWithValue(IDbDataParameter parameter, int index, object value)

Check warning on line 1797 in src/Migrator/Providers/TransformationProvider.cs

View workflow job for this annotation

GitHub Actions / Test (Unit)

'ConfigureParameterWithValue' has a cyclomatic complexity of '28'. Rewrite or refactor the code to decrease its complexity below '26'. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1502)

Check warning on line 1797 in src/Migrator/Providers/TransformationProvider.cs

View workflow job for this annotation

GitHub Actions / Test (SQLite)

'ConfigureParameterWithValue' has a cyclomatic complexity of '28'. Rewrite or refactor the code to decrease its complexity below '26'. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1502)

Check warning on line 1797 in src/Migrator/Providers/TransformationProvider.cs

View workflow job for this annotation

GitHub Actions / Test (MariaDB)

'ConfigureParameterWithValue' has a cyclomatic complexity of '28'. Rewrite or refactor the code to decrease its complexity below '26'. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1502)

Check warning on line 1797 in src/Migrator/Providers/TransformationProvider.cs

View workflow job for this annotation

GitHub Actions / Test (Firebird)

'ConfigureParameterWithValue' has a cyclomatic complexity of '28'. Rewrite or refactor the code to decrease its complexity below '26'. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1502)

Check warning on line 1797 in src/Migrator/Providers/TransformationProvider.cs

View workflow job for this annotation

GitHub Actions / Test (MySQL)

'ConfigureParameterWithValue' has a cyclomatic complexity of '28'. Rewrite or refactor the code to decrease its complexity below '26'. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1502)

Check warning on line 1797 in src/Migrator/Providers/TransformationProvider.cs

View workflow job for this annotation

GitHub Actions / Test (PostgreSQL)

'ConfigureParameterWithValue' has a cyclomatic complexity of '28'. Rewrite or refactor the code to decrease its complexity below '26'. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1502)

Check warning on line 1797 in src/Migrator/Providers/TransformationProvider.cs

View workflow job for this annotation

GitHub Actions / Test (Informix)

'ConfigureParameterWithValue' has a cyclomatic complexity of '28'. Rewrite or refactor the code to decrease its complexity below '26'. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1502)

Check warning on line 1797 in src/Migrator/Providers/TransformationProvider.cs

View workflow job for this annotation

GitHub Actions / Test (Sybase)

'ConfigureParameterWithValue' has a cyclomatic complexity of '28'. Rewrite or refactor the code to decrease its complexity below '26'. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1502)

Check warning on line 1797 in src/Migrator/Providers/TransformationProvider.cs

View workflow job for this annotation

GitHub Actions / Test (Db2)

'ConfigureParameterWithValue' has a cyclomatic complexity of '28'. Rewrite or refactor the code to decrease its complexity below '26'. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1502)

Check warning on line 1797 in src/Migrator/Providers/TransformationProvider.cs

View workflow job for this annotation

GitHub Actions / Test (Oracle)

'ConfigureParameterWithValue' has a cyclomatic complexity of '28'. Rewrite or refactor the code to decrease its complexity below '26'. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1502)

Check warning on line 1797 in src/Migrator/Providers/TransformationProvider.cs

View workflow job for this annotation

GitHub Actions / Test (Hana)

'ConfigureParameterWithValue' has a cyclomatic complexity of '28'. Rewrite or refactor the code to decrease its complexity below '26'. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1502)

Check warning on line 1797 in src/Migrator/Providers/TransformationProvider.cs

View workflow job for this annotation

GitHub Actions / Test (SQLServer)

'ConfigureParameterWithValue' has a cyclomatic complexity of '28'. Rewrite or refactor the code to decrease its complexity below '26'. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1502)
{
if (value == null || value == DBNull.Value)
{
Expand Down
Loading