Skip to content

Commit cec21bc

Browse files
Ja bist du narrischJa bist du narrisch
authored andcommitted
Fix SQLite for compound primary keys
1 parent 72b9105 commit cec21bc

8 files changed

+67
-24
lines changed

src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_AddTableTests.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,11 @@ public void AddTable_CompositePrimaryKey_ContainsNull()
4747

4848
// Assert
4949
var createScript = ((SQLiteTransformationProvider)Provider).GetSqlCreateTableScript(tableName);
50-
Assert.That("CREATE TABLE MyTableName (Column1 INTEGER NOT NULL, Column2 INTEGER NOT NULL, PRIMARY KEY (Column1,Column2) )", Is.EqualTo(createScript));
50+
Assert.That("CREATE TABLE MyTableName (Column1 INTEGER NULL, Column2 INTEGER NOT NULL, PRIMARY KEY (Column1, Column2))", Is.EqualTo(createScript));
5151

5252
var pragmaTableInfos = ((SQLiteTransformationProvider)Provider).GetPragmaTableInfoItems(tableName);
53-
Assert.That(pragmaTableInfos.All(x => x.NotNull), Is.True);
53+
Assert.That(pragmaTableInfos.Single(x => x.Name == columnName1).NotNull, Is.False);
54+
Assert.That(pragmaTableInfos.Single(x => x.Name == columnName2).NotNull, Is.True);
5455

5556
var sqliteInfo = ((SQLiteTransformationProvider)Provider).GetSQLiteTableInfo(tableName);
5657
Assert.That(sqliteInfo.Columns.First().Name, Is.EqualTo(columnName1));

src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_GetColumnsTests.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,14 +70,12 @@ public void GetColumns_PrimaryKeyOnTwoColumns_BothColumnsHavePrimaryKeyAndAreNot
7070
new Column("Id2", System.Data.DbType.Int32, ColumnProperty.PrimaryKey)
7171
);
7272

73-
Provider.GetColumns(tableName);
74-
7573
// Act
7674
var columns = Provider.GetColumns(tableName);
7775

7876
// Assert
79-
Assert.That(columns[0].ColumnProperty, Is.EqualTo(ColumnProperty.PrimaryKey | ColumnProperty.NotNull | ColumnProperty.Identity));
80-
Assert.That(columns[1].ColumnProperty, Is.EqualTo(ColumnProperty.PrimaryKey | ColumnProperty.NotNull));
77+
Assert.That(columns[0].ColumnProperty, Is.EqualTo(ColumnProperty.PrimaryKey | ColumnProperty.Null));
78+
Assert.That(columns[1].ColumnProperty, Is.EqualTo(ColumnProperty.PrimaryKey | ColumnProperty.Null));
8179
}
8280

8381
[Test]
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
using System;
2+
using System.Data;
3+
using System.Linq;
4+
using DotNetProjects.Migrator.Providers.Impl.SQLite;
5+
using Migrator.Framework;
6+
using Migrator.Tests.Providers.SQLite.Base;
7+
using NUnit.Framework;
8+
9+
namespace Migrator.Tests.Providers.SQLite;
10+
11+
[TestFixture]
12+
[Category("SQLite")]
13+
public class SQLiteTransformationProvider_RecreateTableTests : SQLiteTransformationProviderTestBase
14+
{
15+
[Test]
16+
public void RecreateTable_HavingACompoundPrimaryKey_Success()
17+
{
18+
// Arrange
19+
Provider.AddTable("Common_Availability_EvRef",
20+
new Column("EventId", DbType.Int64, ColumnProperty.NotNull | ColumnProperty.PrimaryKey),
21+
new Column("AvailabilityGroupId", DbType.Guid, ColumnProperty.NotNull | ColumnProperty.PrimaryKey));
22+
23+
var sqliteInfo = ((SQLiteTransformationProvider)Provider).GetSQLiteTableInfo("Common_Availability_EvRef");
24+
var sql = ((SQLiteTransformationProvider)Provider).GetSqlCreateTableScript("Common_Availability_EvRef");
25+
26+
// Act/Assert
27+
((SQLiteTransformationProvider)Provider).RecreateTable(sqliteInfo);
28+
var sql2 = ((SQLiteTransformationProvider)Provider).GetSqlCreateTableScript("Common_Availability_EvRef");
29+
30+
31+
Assert.That(sql, Is.EqualTo("CREATE TABLE Common_Availability_EvRef (EventId INTEGER NOT NULL, AvailabilityGroupId UNIQUEIDENTIFIER NOT NULL, PRIMARY KEY (EventId, AvailabilityGroupId))"));
32+
33+
// The quotes around the table name are added by SQLite on ALTER TABLE in RecreateTable
34+
Assert.That(sql2, Is.EqualTo("CREATE TABLE \"Common_Availability_EvRef\" (EventId INTEGER NOT NULL, AvailabilityGroupId UNIQUEIDENTIFIER NOT NULL, PRIMARY KEY (EventId, AvailabilityGroupId))"));
35+
}
36+
}

src/Migrator.Tests/Providers/SQLite/SQLiteTransformationProvider_RenameColumnTests.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System;
12
using System.Data;
23
using System.Linq;
34
using DotNetProjects.Migrator.Providers.Impl.SQLite;

src/Migrator/Providers/Dialect.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,7 @@ public ColumnPropertiesMapper GetAndMapColumnProperties(Column column)
363363
{
364364
var mapper = GetColumnMapper(column);
365365
mapper.MapColumnProperties(column);
366+
366367
if (column.DefaultValue != null && column.DefaultValue != DBNull.Value)
367368
{
368369
mapper.Default = column.DefaultValue;

src/Migrator/Providers/Impl/Mysql/MariaDBTransformationProvider.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Data;
2+
using DotNetProjects.Migrator.Providers.Impl.Mysql;
23

34
namespace Migrator.Providers.Mysql;
45

src/Migrator/Providers/Impl/Mysql/MySqlTransformationProvider.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
using Migrator.Framework;
2+
using Migrator.Providers;
23
using System;
34
using System.Collections.Generic;
45
using System.Data;
56
using System.Globalization;
67
using Index = Migrator.Framework.Index;
78

8-
namespace Migrator.Providers.Mysql;
9+
namespace DotNetProjects.Migrator.Providers.Impl.Mysql;
910

1011
/// <summary>
1112
/// MySql transformation provider

src/Migrator/Providers/Impl/SQLite/SQLiteTransformationProvider.cs

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,11 @@ public override void RemoveColumn(string tableName, string column)
513513

514514
public override void RenameColumn(string tableName, string oldColumnName, string newColumnName)
515515
{
516+
if (!TableExists(tableName))
517+
{
518+
throw new Exception($"Table {tableName} does not exist");
519+
}
520+
516521
var isPragmaForeignKeysOn = IsPragmaForeignKeysOn();
517522

518523
if (isPragmaForeignKeysOn)
@@ -578,15 +583,9 @@ public override void RenameColumn(string tableName, string oldColumnName, string
578583
}
579584

580585
foreignKey.ParentColumns = foreignKey.ParentColumns.Select(x => x == oldColumnName ? newColumnName : x).ToArray();
581-
}
582586

583-
RecreateTable(sqliteTableInfoOther);
584-
}
585-
586-
// Rename column in index
587-
foreach (var index in sqliteTableInfo.Indexes)
588-
{
589-
index.KeyColumns = index.KeyColumns.Select(x => x == oldColumnName ? newColumnName : x).ToArray();
587+
RecreateTable(sqliteTableInfoOther);
588+
}
590589
}
591590
}
592591
else
@@ -700,7 +699,7 @@ public void SetPragmaForeignKeys(bool isOn)
700699
ExecuteQuery(cmd, $"PRAGMA foreign_keys = {onOffString}");
701700
}
702701

703-
private void RecreateTable(SQLiteTableInfo sqliteTableInfo)
702+
public void RecreateTable(SQLiteTableInfo sqliteTableInfo)
704703
{
705704
var sourceTableQuoted = QuoteTableNameIfRequired(sqliteTableInfo.TableNameMapping.OldName);
706705
var targetIntermediateTableQuoted = QuoteTableNameIfRequired($"{sqliteTableInfo.TableNameMapping.NewName}{IntermediateTableSuffix}");
@@ -762,6 +761,7 @@ private void RecreateTable(SQLiteTableInfo sqliteTableInfo)
762761

763762
using (var cmd = CreateCommand())
764763
{
764+
// Rename to original name
765765
var sql = $"ALTER TABLE {targetIntermediateTableQuoted} RENAME TO {targetTableQuoted}";
766766
ExecuteQuery(cmd, sql);
767767
}
@@ -1013,7 +1013,9 @@ public override Column[] GetColumns(string tableName)
10131013

10141014
var columnTableInfoItem = pragmaTableInfoItems.First(x => x.Name.Equals(column.Name, StringComparison.OrdinalIgnoreCase));
10151015

1016-
if (columnTableInfoItem.Type == "INTEGER" && columnTableInfoItem.Pk == 1)
1016+
var hasCompoundPrimaryKey = tableInfoPrimaryKeys.Count > 1;
1017+
1018+
if (columnTableInfoItem.Type == "INTEGER" && columnTableInfoItem.Pk == 1 && !hasCompoundPrimaryKey)
10171019
{
10181020
column.ColumnProperty |= ColumnProperty.Identity;
10191021
}
@@ -1088,17 +1090,19 @@ public override void AddTable(string name, string engine, params IDbField[] fiel
10881090
.ToArray();
10891091

10901092
var pks = GetPrimaryKeys(columns);
1091-
var compoundPrimaryKey = pks.Count > 1;
1093+
var hasCompoundPrimaryKey = pks.Count > 1;
10921094

10931095
var columnProviders = new List<ColumnPropertiesMapper>(columns.Length);
10941096

10951097
foreach (var column in columns)
10961098
{
1097-
// Remove the primary key notation if compound primary key because we'll add it back later
1098-
if (compoundPrimaryKey && column.IsPrimaryKey)
1099+
if (hasCompoundPrimaryKey && column.IsPrimaryKey)
10991100
{
1100-
column.ColumnProperty ^= ColumnProperty.PrimaryKey;
1101-
column.ColumnProperty |= ColumnProperty.NotNull; // PK is always not-null
1101+
// We remove PrimaryKey here and readd it as compound later ("...PRIMARY KEY(column1,column2)");
1102+
column.ColumnProperty &= ~ColumnProperty.PrimaryKey;
1103+
1104+
// AUTOINCREMENT cannot be used in compound primary keys in SQLite so we remove Identity here
1105+
column.ColumnProperty &= ~ColumnProperty.Identity;
11021106
}
11031107

11041108
var mapper = _dialect.GetAndMapColumnProperties(column);
@@ -1112,9 +1116,9 @@ public override void AddTable(string name, string engine, params IDbField[] fiel
11121116

11131117
stringBuilder.Append(string.Format("CREATE TABLE {0} ({1}", table, columnsAndIndexes));
11141118

1115-
if (compoundPrimaryKey)
1119+
if (hasCompoundPrimaryKey)
11161120
{
1117-
stringBuilder.Append(string.Format(", PRIMARY KEY ({0}) ", string.Join(",", pks.ToArray())));
1121+
stringBuilder.Append(string.Format(", PRIMARY KEY ({0})", string.Join(", ", pks.ToArray())));
11181122
}
11191123

11201124
var uniques = fields.Where(x => x is Unique).Cast<Unique>().ToArray();

0 commit comments

Comments
 (0)