Skip to content

Commit

Permalink
Merge pull request #1758 from jzabroski/fixSqliteMultiplePrimaryKeys
Browse files Browse the repository at this point in the history
Fix sqlite multiple primary keys
  • Loading branch information
jzabroski committed Mar 27, 2024
2 parents a8f6068 + 469f793 commit ce086ce
Show file tree
Hide file tree
Showing 33 changed files with 412 additions and 229 deletions.
16 changes: 14 additions & 2 deletions src/FluentMigrator.Runner.SQLite/Generators/SQLite/SQLiteColumn.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,21 +26,33 @@ public SQLiteColumn(IQuoter quoter, ISQLiteTypeMap typeMap)
/// <inheritdoc />
public override string Generate(IEnumerable<ColumnDefinition> columns, string tableName)
{
var primaryKeyString = string.Empty;

var colDefs = columns.ToList();
var foreignKeyColumns = colDefs.Where(x => x.IsForeignKey && x.ForeignKey != null).ToList();
var foreignKeyClauses = foreignKeyColumns
.Select(x => ", " + FormatForeignKey(x.ForeignKey, GenerateForeignKeyName))
.ToList();

// As we generate FKs as part of the create table statement we need a way to prevent
// As we generate FKs as part of the CREATE TABLE statement we need a way to prevent
// these FK's from creating FK constraints (which SQLite doesn't support) so we prefix
// the FK name and ignore anything in the "CreateConstraint" handler that has this name prefix
foreach (var fk in foreignKeyColumns) {
fk.ForeignKey.Name = "$$IGNORE$$_" + fk.ForeignKey.Name;
}

/*
var primaryKeyColumns = colDefs.Where(x => x.IsPrimaryKey);
var pkColDefs = primaryKeyColumns.ToList();
if (ShouldPrimaryKeysBeAddedSeparately(pkColDefs))
{
primaryKeyString = AddPrimaryKeyConstraint(tableName, pkColDefs);
foreach (var column in colDefs) { column.IsPrimaryKey = false; }
}
*/
// Append foreign key definitions after all column definitions and the primary key definition
return base.Generate(colDefs, tableName) + string.Concat(foreignKeyClauses);
return base.Generate(colDefs, tableName) + primaryKeyString + string.Concat(foreignKeyClauses);
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ public SQLiteGenerator()
public SQLiteGenerator(
[NotNull] SQLiteQuoter quoter,
[NotNull] IOptions<GeneratorOptions> generatorOptions)
// ReSharper disable once RedundantArgumentDefaultValue
: this(quoter, new SQLiteTypeMap(false), generatorOptions)
{
}
Expand Down Expand Up @@ -111,26 +112,33 @@ public override string Generate(DeleteDefaultConstraintExpression expression)

public override string Generate(CreateConstraintExpression expression)
{
if (!expression.Constraint.IsUniqueConstraint)
return CompatibilityMode.HandleCompatibilty("Only UNIQUE constraints are supported in SQLite");
if (!(expression.Constraint.IsUniqueConstraint || expression.Constraint.IsPrimaryKeyConstraint))
{
return CompatibilityMode.HandleCompatibilty("Only creating UNIQUE and PRIMARY KEY constraints are supported in SQLite");
}

// Convert the constraint into a UNIQUE index
var idx = new CreateIndexExpression();
idx.Index.Name = expression.Constraint.ConstraintName;
idx.Index.TableName = expression.Constraint.TableName;
idx.Index.SchemaName = expression.Constraint.SchemaName;
idx.Index.IsUnique = true;
if (expression.Constraint.IsUniqueConstraint)
{
// Convert the constraint into a UNIQUE index
var idx = new CreateIndexExpression();
idx.Index.Name = expression.Constraint.ConstraintName;
idx.Index.TableName = expression.Constraint.TableName;
idx.Index.SchemaName = expression.Constraint.SchemaName;
idx.Index.IsUnique = true;

foreach (var col in expression.Constraint.Columns)
idx.Index.Columns.Add(new IndexColumnDefinition { Name = col });
foreach (var col in expression.Constraint.Columns)
idx.Index.Columns.Add(new IndexColumnDefinition { Name = col });

return Generate(idx);
return Generate(idx);
}

return base.Generate(expression);
}

public override string Generate(DeleteConstraintExpression expression)
{
if (!expression.Constraint.IsUniqueConstraint)
return CompatibilityMode.HandleCompatibilty("Only UNIQUE constraints are supported in SQLite");
return CompatibilityMode.HandleCompatibilty("Only deleting UNIQUE constraints are supported in SQLite");

// Convert the constraint into a drop UNIQUE index
var idx = new DeleteIndexExpression();
Expand Down
18 changes: 10 additions & 8 deletions test/FluentMigrator.Tests/Unit/Generators/BaseConstraintsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
//
#endregion

using FluentMigrator.Runner;

using NUnit.Framework;

namespace FluentMigrator.Tests.Unit.Generators
Expand All @@ -30,8 +32,8 @@ public abstract class BaseConstraintsTests
public abstract void CanCreateMultiColumnForeignKeyWithCustomSchema();
public abstract void CanCreateMultiColumnForeignKeyWithDefaultSchema();
public abstract void CanCreateMultiColumnForeignKeyWithDifferentSchemas();
public abstract void CanCreateMultiColumnPrimaryKeyConstraintWithCustomSchema();
public abstract void CanCreateMultiColumnPrimaryKeyConstraintWithDefaultSchema();
public abstract void CanCreateMultiColumnPrimaryKeyConstraintWithCustomSchema([Values] CompatibilityMode compatibilityMode);
public abstract void CanCreateMultiColumnPrimaryKeyConstraintWithDefaultSchema([Values] CompatibilityMode compatibilityMode);
public abstract void CanCreateMultiColumnUniqueConstraintWithCustomSchema();
public abstract void CanCreateMultiColumnUniqueConstraintWithDefaultSchema();
public abstract void CanCreateNamedForeignKeyWithCustomSchema();
Expand All @@ -43,16 +45,16 @@ public abstract class BaseConstraintsTests
public abstract void CanCreateNamedMultiColumnForeignKeyWithCustomSchema();
public abstract void CanCreateNamedMultiColumnForeignKeyWithDefaultSchema();
public abstract void CanCreateNamedMultiColumnForeignKeyWithDifferentSchemas();
public abstract void CanCreateNamedMultiColumnPrimaryKeyConstraintWithCustomSchema();
public abstract void CanCreateNamedMultiColumnPrimaryKeyConstraintWithDefaultSchema();
public abstract void CanCreateNamedMultiColumnPrimaryKeyConstraintWithCustomSchema([Values] CompatibilityMode compatibilityMode);
public abstract void CanCreateNamedMultiColumnPrimaryKeyConstraintWithDefaultSchema([Values] CompatibilityMode compatibilityMode);
public abstract void CanCreateNamedMultiColumnUniqueConstraintWithCustomSchema();
public abstract void CanCreateNamedMultiColumnUniqueConstraintWithDefaultSchema();
public abstract void CanCreateNamedPrimaryKeyConstraintWithCustomSchema();
public abstract void CanCreateNamedPrimaryKeyConstraintWithDefaultSchema();
public abstract void CanCreateNamedPrimaryKeyConstraintWithCustomSchema([Values] CompatibilityMode compatibilityMode);
public abstract void CanCreateNamedPrimaryKeyConstraintWithDefaultSchema([Values] CompatibilityMode compatibilityMode);
public abstract void CanCreateNamedUniqueConstraintWithCustomSchema();
public abstract void CanCreateNamedUniqueConstraintWithDefaultSchema();
public abstract void CanCreatePrimaryKeyConstraintWithCustomSchema();
public abstract void CanCreatePrimaryKeyConstraintWithDefaultSchema();
public abstract void CanCreatePrimaryKeyConstraintWithCustomSchema([Values] CompatibilityMode compatibilityMode);
public abstract void CanCreatePrimaryKeyConstraintWithDefaultSchema([Values] CompatibilityMode compatibilityMode);
public abstract void CanCreateUniqueConstraintWithCustomSchema();
public abstract void CanCreateUniqueConstraintWithDefaultSchema();
public abstract void CanDropForeignKeyWithCustomSchema();
Expand Down
4 changes: 3 additions & 1 deletion test/FluentMigrator.Tests/Unit/Generators/BaseTableTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
//
#endregion

using FluentMigrator.Runner;

using NUnit.Framework;

namespace FluentMigrator.Tests.Unit.Generators
Expand All @@ -35,7 +37,7 @@ public abstract class BaseTableTests
public abstract void CanCreateTableWithIdentityWithCustomSchema();
public abstract void CanCreateTableWithIdentityWithDefaultSchema();
public abstract void CanCreateTableWithMultiColumnPrimaryKeyWithCustomSchema();
public abstract void CanCreateTableWithMultiColumnPrimaryKeyWithDefaultSchema();
public abstract void CanCreateTableWithMultiColumnPrimaryKeyWithDefaultSchema(CompatibilityMode compatibilityMode);
public abstract void CanCreateTableWithNamedMultiColumnPrimaryKeyWithCustomSchema();
public abstract void CanCreateTableWithNamedMultiColumnPrimaryKeyWithDefaultSchema();
public abstract void CanCreateTableWithNamedPrimaryKeyWithCustomSchema();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

using System.Data;

using FluentMigrator.Runner;
using FluentMigrator.Runner.Generators;
using FluentMigrator.Runner.Generators.DB2;
using FluentMigrator.Runner.Generators.DB2.iSeries;
Expand Down Expand Up @@ -103,20 +104,22 @@ public override void CanCreateMultiColumnForeignKeyWithDifferentSchemas()
}

[Test]
public override void CanCreateMultiColumnPrimaryKeyConstraintWithCustomSchema()
public override void CanCreateMultiColumnPrimaryKeyConstraintWithCustomSchema([Values] CompatibilityMode compatibilityMode)
{
var expression = GeneratorTestHelper.GetCreateMultiColumnPrimaryKeyExpression();
expression.Constraint.SchemaName = "TestSchema";

Generator.CompatibilityMode = compatibilityMode;
var result = Generator.Generate(expression);
result.ShouldBe("ALTER TABLE TestSchema.TestTable1 ADD CONSTRAINT TestSchema.PK_TestTable1_TestColumn1_TestColumn2 PRIMARY KEY (TestColumn1, TestColumn2)");
}

[Test]
public override void CanCreateMultiColumnPrimaryKeyConstraintWithDefaultSchema()
public override void CanCreateMultiColumnPrimaryKeyConstraintWithDefaultSchema([Values] CompatibilityMode compatibilityMode)
{
var expression = GeneratorTestHelper.GetCreateMultiColumnPrimaryKeyExpression();

Generator.CompatibilityMode = compatibilityMode;
var result = Generator.Generate(expression);
result.ShouldBe("ALTER TABLE TestTable1 ADD CONSTRAINT PK_TestTable1_TestColumn1_TestColumn2 PRIMARY KEY (TestColumn1, TestColumn2)");
}
Expand Down Expand Up @@ -232,20 +235,22 @@ public override void CanCreateNamedMultiColumnForeignKeyWithDifferentSchemas()
}

[Test]
public override void CanCreateNamedMultiColumnPrimaryKeyConstraintWithCustomSchema()
public override void CanCreateNamedMultiColumnPrimaryKeyConstraintWithCustomSchema([Values] CompatibilityMode compatibilityMode)
{
var expression = GeneratorTestHelper.GetCreateNamedMultiColumnPrimaryKeyExpression();
expression.Constraint.SchemaName = "TestSchema";

Generator.CompatibilityMode = compatibilityMode;
var result = Generator.Generate(expression);
result.ShouldBe("ALTER TABLE TestSchema.TestTable1 ADD CONSTRAINT TestSchema.TESTPRIMARYKEY PRIMARY KEY (TestColumn1, TestColumn2)");
}

[Test]
public override void CanCreateNamedMultiColumnPrimaryKeyConstraintWithDefaultSchema()
public override void CanCreateNamedMultiColumnPrimaryKeyConstraintWithDefaultSchema([Values] CompatibilityMode compatibilityMode)
{
var expression = GeneratorTestHelper.GetCreateNamedMultiColumnPrimaryKeyExpression();

Generator.CompatibilityMode = compatibilityMode;
var result = Generator.Generate(expression);
result.ShouldBe("ALTER TABLE TestTable1 ADD CONSTRAINT TESTPRIMARYKEY PRIMARY KEY (TestColumn1, TestColumn2)");
}
Expand All @@ -270,20 +275,22 @@ public override void CanCreateNamedMultiColumnUniqueConstraintWithDefaultSchema(
}

[Test]
public override void CanCreateNamedPrimaryKeyConstraintWithCustomSchema()
public override void CanCreateNamedPrimaryKeyConstraintWithCustomSchema([Values] CompatibilityMode compatibilityMode)
{
var expression = GeneratorTestHelper.GetCreateNamedPrimaryKeyExpression();
expression.Constraint.SchemaName = "TestSchema";

Generator.CompatibilityMode = compatibilityMode;
var result = Generator.Generate(expression);
result.ShouldBe("ALTER TABLE TestSchema.TestTable1 ADD CONSTRAINT TestSchema.TESTPRIMARYKEY PRIMARY KEY (TestColumn1)");
}

[Test]
public override void CanCreateNamedPrimaryKeyConstraintWithDefaultSchema()
public override void CanCreateNamedPrimaryKeyConstraintWithDefaultSchema([Values] CompatibilityMode compatibilityMode)
{
var expression = GeneratorTestHelper.GetCreateNamedPrimaryKeyExpression();

Generator.CompatibilityMode = compatibilityMode;
var result = Generator.Generate(expression);
result.ShouldBe("ALTER TABLE TestTable1 ADD CONSTRAINT TESTPRIMARYKEY PRIMARY KEY (TestColumn1)");
}
Expand All @@ -308,20 +315,22 @@ public override void CanCreateNamedUniqueConstraintWithDefaultSchema()
}

[Test]
public override void CanCreatePrimaryKeyConstraintWithCustomSchema()
public override void CanCreatePrimaryKeyConstraintWithCustomSchema([Values] CompatibilityMode compatibilityMode)
{
var expression = GeneratorTestHelper.GetCreatePrimaryKeyExpression();
expression.Constraint.SchemaName = "TestSchema";

Generator.CompatibilityMode = compatibilityMode;
var result = Generator.Generate(expression);
result.ShouldBe("ALTER TABLE TestSchema.TestTable1 ADD CONSTRAINT TestSchema.PK_TestTable1_TestColumn1 PRIMARY KEY (TestColumn1)");
}

[Test]
public override void CanCreatePrimaryKeyConstraintWithDefaultSchema()
public override void CanCreatePrimaryKeyConstraintWithDefaultSchema([Values] CompatibilityMode compatibilityMode)
{
var expression = GeneratorTestHelper.GetCreatePrimaryKeyExpression();

Generator.CompatibilityMode = compatibilityMode;
var result = Generator.Generate(expression);
result.ShouldBe("ALTER TABLE TestTable1 ADD CONSTRAINT PK_TestTable1_TestColumn1 PRIMARY KEY (TestColumn1)");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,10 +137,11 @@ public override void CanCreateTableWithMultiColumnPrimaryKeyWithCustomSchema()
}

[Test]
public override void CanCreateTableWithMultiColumnPrimaryKeyWithDefaultSchema()
public override void CanCreateTableWithMultiColumnPrimaryKeyWithDefaultSchema([Values] CompatibilityMode compatibilityMode)
{
var expression = GeneratorTestHelper.GetCreateTableWithMultiColumnPrimaryKeyExpression();

Generator.CompatibilityMode = compatibilityMode;
var result = Generator.Generate(expression);
result.ShouldBe("CREATE TABLE TestTable1 (TestColumn1 DBCLOB(1048576) CCSID 1200 NOT NULL, TestColumn2 INTEGER NOT NULL, PRIMARY KEY (TestColumn1, TestColumn2))");
}
Expand Down Expand Up @@ -254,7 +255,7 @@ public void CanDropTableIfExistsWithCustomSchema()
public override void CanDropTableIfExistsWithDefaultSchema()
{
var expression = GeneratorTestHelper.GetDeleteTableIfExistsExpression();
Generator.compatabilityMode = CompatabilityMode.STRICT;
Generator.CompatibilityMode = CompatibilityMode.STRICT;

Assert.Throws<DatabaseOperationNotSupportedException>(() => Generator.Generate(expression));
}
Expand Down

0 comments on commit ce086ce

Please sign in to comment.