diff --git a/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_AddColumnTests.cs b/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_AddColumnTests.cs
index b9d32893..4b9c6453 100644
--- a/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_AddColumnTests.cs
+++ b/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_AddColumnTests.cs
@@ -59,8 +59,11 @@ public void AddColumn_HavingColumnPropertyUniqueAndIndex_RebuildSucceeds()
CollectionAssert.AreEquivalent(indexAfter.KeyColumns, new string[] { propertyName1, propertyName2 });
}
+ ///
+ /// NOT NULL is implicitly set by the migrator for non-composite primary key
+ ///
[Test]
- public void AddColumn_HavingNullInPrimaryKey_HasNULLAfterAddAnotherColumn()
+ public void AddColumn_HavingNullInPrimaryKey_HasNotNullAfterAddAnotherColumn()
{
// Arrange/Act
Provider.ExecuteNonQuery("CREATE TABLE Common_Language (LanguageID TEXT PRIMARY KEY)");
@@ -73,7 +76,7 @@ public void AddColumn_HavingNullInPrimaryKey_HasNULLAfterAddAnotherColumn()
var columnProperty = tableInfo.Columns.Single(x => x.Name == "LanguageID").ColumnProperty;
// Assert
- Assert.That(script, Does.Contain("LanguageID TEXT NULL PRIMARY KEY"));
+ Assert.That(script, Does.Contain("LanguageID TEXT NOT NULL PRIMARY KEY"));
}
[Test]
diff --git a/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_AddPrimaryKeyTests.cs b/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_AddPrimaryKeyTests.cs
index ccde9404..23c217e8 100644
--- a/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_AddPrimaryKeyTests.cs
+++ b/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_AddPrimaryKeyTests.cs
@@ -1,4 +1,6 @@
+using System;
using System.Data;
+using System.Data.SQLite;
using System.Threading.Tasks;
using DotNetProjects.Migrator.Framework;
using DotNetProjects.Migrator.Providers.Impl.SQLite;
@@ -27,7 +29,10 @@ public void AddPrimaryKey_ColumnsInOtherOrderThanInColumnsList_Success()
const string tableName = "TestTable";
const string primaryKeyName = $"PK_{tableName}";
- Provider.AddTable(tableName, new Column(columnName1, DbType.String), new Column(columnName2, DbType.Int32), new Column(columnName3, DbType.Int32));
+ Provider.AddTable(tableName,
+ new Column(columnName1, DbType.String),
+ new Column(columnName2, DbType.Int32),
+ new Column(columnName3, DbType.Int32));
// Act
Provider.AddPrimaryKey(name: primaryKeyName, table: tableName, columns: [columnName3, columnName2]);
@@ -37,4 +42,55 @@ public void AddPrimaryKey_ColumnsInOtherOrderThanInColumnsList_Success()
Assert.That(createTableScript, Does.Contain("PRIMARY KEY (TestColumn3, TestColumn2))"));
}
+
+ [Test]
+ public void AddPrimaryKey_ColumnGuidNonComposite_ThrowsOnDuplicatesAndNulls()
+ {
+ const string tableName = "MyTableName";
+ const string columnName1 = "Column1";
+ var guid = Guid.NewGuid();
+
+ // Arrange/Act
+ Provider.AddTable(tableName,
+ new Column(columnName1, DbType.Guid, ColumnProperty.PrimaryKey)
+ );
+
+ Provider.Insert(tableName, [columnName1], [guid]);
+ Assert.Throws(() => Provider.Insert(tableName, [columnName1], [guid]));
+ Assert.Throws(() => Provider.Insert(tableName, [columnName1], [null]));
+ }
+
+ [Test]
+ public void AddPrimaryKey_ColumnGuidComposite_ThrowsOnDuplicatesAndNulls()
+ {
+ // Arrange
+ const string columnName1 = "TestColumn1";
+ const string columnName2 = "TestColumn2";
+ const string tableName = "TestTable";
+ const string primaryKeyName = $"PK_{tableName}";
+ var guid = Guid.NewGuid();
+ var guid2 = Guid.NewGuid();
+
+ Provider.AddTable(tableName,
+ new Column(columnName1, DbType.Guid),
+ new Column(columnName2, DbType.Guid));
+
+ // Act
+ Provider.AddPrimaryKey(name: primaryKeyName, table: tableName, columns: [columnName1, columnName2]);
+
+ // This is a normal SQLite behavior!
+ // NULL != NULL
+ // (A, NULL) != (A, NULL)
+ // Duplicates! You need to set NotNull if you want to prevent it!
+ Provider.Insert(tableName, [columnName1, columnName2], [guid, null]);
+
+ Provider.Insert(tableName, [columnName1, columnName2], [guid, null]);
+ Provider.Insert(tableName, [columnName1, columnName2], [guid, null]);
+
+ Provider.Insert(tableName, [columnName1, columnName2], [null, guid]);
+ Provider.Insert(tableName, [columnName1, columnName2], [null, guid]);
+
+ Provider.Insert(tableName, [columnName1, columnName2], [guid2, guid2]);
+ Assert.Throws(() => Provider.Insert(tableName, [columnName1, columnName2], [guid2, guid2]));
+ }
}
diff --git a/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_AddTableTests.cs b/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_AddTableTests.cs
index 2b9eee06..1cc48749 100644
--- a/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_AddTableTests.cs
+++ b/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_AddTableTests.cs
@@ -1,3 +1,4 @@
+using System;
using System.Data.SQLite;
using System.Linq;
using System.Threading.Tasks;
@@ -89,7 +90,7 @@ public void AddTable_SinglePrimaryKey_ContainsNull()
var createScript = ((SQLiteTransformationProvider)Provider).GetSqlCreateTableScript(tableName);
// In SQLite an INTEGER PRIMARY KEY column is NOT NULL implicitly (see insert asserts above)
- Assert.That(createScript, Is.EqualTo("CREATE TABLE MyTableName (Column1 INTEGER PRIMARY KEY, Column2 INTEGER NOT NULL)"));
+ Assert.That(createScript, Is.EqualTo("CREATE TABLE MyTableName (Column1 INTEGER NOT NULL PRIMARY KEY, Column2 INTEGER NOT NULL)"));
var sqliteInfo = ((SQLiteTransformationProvider)Provider).GetSQLiteTableInfo(tableName);
Assert.That(sqliteInfo.Columns.First().Name, Is.EqualTo(columnName1));
@@ -124,4 +125,58 @@ public void AddTable_MiscellaneousColumns_Succeeds()
Assert.That(sqliteInfo.Columns.First().Name, Is.EqualTo(columnName1));
Assert.That(sqliteInfo.Columns[1].Name, Is.EqualTo(columnName2));
}
+
+ ///
+ /// NOT NULL is implicitly set by SQLite
+ ///
+ [Test]
+ public void AddTable_GuidPrimaryKeyOneColumnPKImplicitlyUsingNotNull_ThrowsOnNullAndOnDuplicates()
+ {
+ const string tableName = "MyTableName";
+ const string columnName1 = "Column1";
+ var guid = Guid.NewGuid();
+
+ // Arrange/Act
+ Provider.AddTable(tableName,
+ new Column(columnName1, System.Data.DbType.Guid, ColumnProperty.PrimaryKey)
+ );
+
+ Provider.Insert(tableName, [columnName1], [guid]);
+ Assert.Throws(() => Provider.Insert(tableName, [columnName1], [guid]));
+
+ // The migrator sets NotNull on PrimaryKey (non composite) so this line throws.
+ Assert.Throws(() => Provider.Insert(tableName, [columnName1], [null]));
+ }
+
+ ///
+ /// Composite PK with Guids
+ ///
+ [Test]
+ public void AddTable_GuidPrimaryKeyCompositeWithGuid_DoesNotThrowOnDuplicateNULLEntries()
+ {
+ const string tableName = "MyTableName";
+ const string columnName1 = "Column1";
+ const string columnName2 = "Column2";
+ var guid = Guid.NewGuid();
+ var guid2 = Guid.NewGuid();
+
+ // Arrange/Act
+ Provider.AddTable(tableName,
+ new Column(columnName1, System.Data.DbType.Guid, ColumnProperty.PrimaryKey),
+ new Column(columnName2, System.Data.DbType.Guid, ColumnProperty.PrimaryKey)
+ );
+
+ // This is a normal SQLite behavior!
+ // NULL != NULL
+ // (A, NULL) != (A, NULL)
+ // Duplicates! You need to set NotNull if you want to prevent it!
+ Provider.Insert(tableName, [columnName1, columnName2], [guid, null]);
+ Provider.Insert(tableName, [columnName1, columnName2], [guid, null]);
+
+ Provider.Insert(tableName, [columnName1, columnName2], [null, guid]);
+ Provider.Insert(tableName, [columnName1, columnName2], [null, guid]);
+
+ Provider.Insert(tableName, [columnName1, columnName2], [guid2, guid2]);
+ Assert.Throws(() => Provider.Insert(tableName, [columnName1, columnName2], [guid2, guid2]));
+ }
}
\ No newline at end of file
diff --git a/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_PropertyColumnIdentityTests.cs b/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_PropertyColumnIdentityTests.cs
index ad841b31..7ba49518 100644
--- a/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_PropertyColumnIdentityTests.cs
+++ b/src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_PropertyColumnIdentityTests.cs
@@ -26,6 +26,6 @@ public void AddPrimaryIdentity_Succeeds()
var sql = ((SQLiteTransformationProvider)Provider).GetSqlCreateTableScript(testTableName);
// NOT NULL implicitly set in SQLite
- Assert.That(sql, Does.Contain("Color1 INTEGER PRIMARY KEY"));
+ Assert.That(sql, Does.Contain("Color1 INTEGER NOT NULL PRIMARY KEY"));
}
}
diff --git a/src/Migrator/Providers/Impl/SQLite/SQLiteTransformationProvider.cs b/src/Migrator/Providers/Impl/SQLite/SQLiteTransformationProvider.cs
index b3fb46a2..7697b05f 100644
--- a/src/Migrator/Providers/Impl/SQLite/SQLiteTransformationProvider.cs
+++ b/src/Migrator/Providers/Impl/SQLite/SQLiteTransformationProvider.cs
@@ -1390,6 +1390,13 @@ public override void AddTable(string name, string engine, params IDbField[] fiel
foreach (var column in columns)
{
+ if (!hasCompoundPrimaryKey && column.IsPrimaryKey)
+ {
+ // We implicitly set NOT NULL for non-composite primary keys like in other RDBMS.
+ column.ColumnProperty = column.ColumnProperty.Clear(ColumnProperty.Null);
+ column.ColumnProperty = column.ColumnProperty.Set(ColumnProperty.NotNull);
+ }
+
if (hasCompoundPrimaryKey && column.IsPrimaryKey)
{
// We remove PrimaryKey here and readd it as compound later ("...PRIMARY KEY(column1,column2)");
diff --git a/src/Migrator/Providers/Impl/SqlServer/SqlServerTransformationProvider.cs b/src/Migrator/Providers/Impl/SqlServer/SqlServerTransformationProvider.cs
index 9aac8aa8..871c2cb8 100644
--- a/src/Migrator/Providers/Impl/SqlServer/SqlServerTransformationProvider.cs
+++ b/src/Migrator/Providers/Impl/SqlServer/SqlServerTransformationProvider.cs
@@ -391,8 +391,6 @@ ORDER BY
var schemaNameOrdinal = reader.GetOrdinal("SchemaName");
var tableNameOrdinal = reader.GetOrdinal("TableName");
-
-
while (reader.Read())
{
var indexItem = new IndexItem