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
2 changes: 1 addition & 1 deletion .github/workflows/integration-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ on:
jobs:
microsoft_sql:
if: github.event.pull_request.draft == false
runs-on: ubuntu-latest
runs-on: ubuntu-20.04
env:
RunDockerTests: true
steps:
Expand Down
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Authors>TiCodeX</Authors>
<Version>2024.12.1</Version>
<Version>2025.3.1</Version>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
<ImplicitUsings>enable</ImplicitUsings>
<IncludeServicesUsings>true</IncludeServicesUsings>
Expand Down
10 changes: 10 additions & 0 deletions SQLSchemaCompare.Core/Entities/Database/ABaseDbIndex.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,19 @@ public class ABaseDbIndex : ABaseDbConstraint
/// <remarks>Used only by the DatabaseProvider to group the indexes and fill the ColumnDescending list</remarks>
public bool IsDescending { get; set; }

/// <summary>
/// Gets or sets a value indicating whether the index is included.
/// </summary>
public bool IsIncluded { get; set; }

/// <summary>
/// Gets whether the column is descending, sorted like the ColumnNames list
/// </summary>
public List<bool> ColumnDescending { get; } = new List<bool>();

/// <summary>
/// Gets the included columns.
/// </summary>
public List<string> IncludedColumns { get; } = new List<string>();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -215,8 +215,9 @@ protected TDatabase DiscoverDatabase(TDatabaseContext context, TaskInfo taskInfo
{
var index = indexGroup.First();
index.Database = db;
index.ColumnNames.AddRange(indexGroup.OrderBy(x => x.OrdinalPosition).Select(x => x.ColumnName));
index.ColumnDescending.AddRange(indexGroup.OrderBy(x => x.OrdinalPosition).Select(x => x.IsDescending));
index.ColumnNames.AddRange(indexGroup.Where(x => !x.IsIncluded).OrderBy(x => x.OrdinalPosition).Select(x => x.ColumnName));
index.ColumnDescending.AddRange(indexGroup.Where(x => !x.IsIncluded).OrderBy(x => x.OrdinalPosition).Select(x => x.IsDescending));
index.IncludedColumns.AddRange(indexGroup.Where(x => x.IsIncluded).OrderBy(x => x.OrdinalPosition).Select(x => x.ColumnName));
db.Indexes.Add(index);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ protected override IEnumerable<ABaseDbIndex> GetIndexes(MicrosoftSqlDatabaseCont
query.AppendLine(" CAST(ic.key_ordinal AS bigint) AS 'OrdinalPosition',");
query.AppendLine(" i.type AS Type,");
query.AppendLine(" i.is_unique AS 'IsUnique',");
query.AppendLine(" ic.is_included_column AS 'IsIncluded',");
query.AppendLine(" i.filter_definition AS 'FilterDefinition'");
query.AppendLine("FROM sys.indexes i");
query.AppendLine("JOIN sys.index_columns ic ON i.object_id = ic.object_id AND i.index_id = ic.index_id");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,12 @@ protected override string ScriptCreateIndex(ABaseDbIndex index)
}

sb.AppendLine($"INDEX {this.ScriptHelper.ScriptObjectName(index.Name)} ON {this.ScriptHelper.ScriptObjectName(index.TableSchema, index.TableName)}({string.Join(",", columnList)})");

if (index.IncludedColumns.Any())
{
sb.AppendLine($"INCLUDE({string.Join(",", index.IncludedColumns.Select(this.ScriptHelper.ScriptObjectName))})");
}

if (!string.IsNullOrWhiteSpace(indexMicrosoft.FilterDefinition))
{
sb.AppendLine($"{Indent}WHERE {indexMicrosoft.FilterDefinition}");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,8 @@ ALTER TABLE customer_data.address ADD CONSTRAINT [DF_address_last_update] DEFAUL
GO
CREATE INDEX idx_fk_city_id ON customer_data.address(city_id)
GO
CREATE INDEX idx_address_address2_district_postal_code ON customer_data.address(address, address2) INCLUDE(district, postal_code)
GO
ALTER TABLE customer_data.address WITH NOCHECK ADD CONSTRAINT fk_address_city FOREIGN KEY (city_id) REFERENCES customer_data.city (city_id) ON DELETE NO ACTION ON UPDATE CASCADE
GO
ALTER TABLE customer_data.address NOCHECK CONSTRAINT fk_address_city
Expand Down
46 changes: 46 additions & 0 deletions SQLSchemaCompare.Test/Integration/MicrosoftSqlTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,21 @@ public void MigrateMicrosoftSqlDatabaseTargetMissingIndex(ushort port)
this.dbFixture.AlterTargetDatabaseExecuteFullAndAllAlterScriptsAndCompare(DatabaseType.MicrosoftSql, sb.ToString(), port);
}

/// <summary>
/// Test migration script when target db doesn't have a index with included columns
/// </summary>
/// <param name="port">The port of the server</param>
[Theory]
[MemberData(nameof(DatabaseFixtureMicrosoftSql.ServerPorts), MemberType = typeof(DatabaseFixtureMicrosoftSql))]
[IntegrationTest]
[Category("MicrosoftSQL")]
public void MigrateMicrosoftSqlDatabaseTargetMissingIndexWithIncludedColumns(ushort port)
{
var sb = new StringBuilder();
sb.AppendLine("DROP INDEX idx_address_address2_district_postal_code ON customer_data.address");
this.dbFixture.AlterTargetDatabaseExecuteFullAndAllAlterScriptsAndCompare(DatabaseType.MicrosoftSql, sb.ToString(), port);
}

/// <summary>
/// Test migration script when target db have an additional index
/// </summary>
Expand All @@ -455,6 +470,21 @@ public void MigrateMicrosoftSqlDatabaseTargetExtraIndex(ushort port)
this.dbFixture.AlterTargetDatabaseExecuteFullAndAllAlterScriptsAndCompare(DatabaseType.MicrosoftSql, sb.ToString(), port);
}

/// <summary>
/// Test migration script when target db have an additional index with included columns
/// </summary>
/// <param name="port">The port of the server</param>
[Theory]
[MemberData(nameof(DatabaseFixtureMicrosoftSql.ServerPorts), MemberType = typeof(DatabaseFixtureMicrosoftSql))]
[IntegrationTest]
[Category("MicrosoftSQL")]
public void MigrateMicrosoftSqlDatabaseTargetExtraIndexWithIncludedColumns(ushort port)
{
var sb = new StringBuilder();
sb.AppendLine("CREATE INDEX idx_title_release_year_special_features ON inventory.film (title, release_year) INCLUDE (special_features)");
this.dbFixture.AlterTargetDatabaseExecuteFullAndAllAlterScriptsAndCompare(DatabaseType.MicrosoftSql, sb.ToString(), port);
}

/// <summary>
/// Test migration script when target db have a different filtered index
/// </summary>
Expand Down Expand Up @@ -487,6 +517,22 @@ public void MigrateMicrosoftSqlDatabaseTargetDifferentIndexType(ushort port)
this.dbFixture.AlterTargetDatabaseExecuteFullAndAllAlterScriptsAndCompare(DatabaseType.MicrosoftSql, sb.ToString(), port);
}

/// <summary>
/// Test migration script when target db have a different index included columns
/// </summary>
/// <param name="port">The port of the server</param>
[Theory]
[MemberData(nameof(DatabaseFixtureMicrosoftSql.ServerPorts), MemberType = typeof(DatabaseFixtureMicrosoftSql))]
[IntegrationTest]
[Category("MicrosoftSQL")]
public void MigrateMicrosoftSqlDatabaseTargetDifferentIndexIncludedColumns(ushort port)
{
var sb = new StringBuilder();
sb.AppendLine("DROP INDEX idx_address_address2_district_postal_code ON customer_data.address");
sb.AppendLine("CREATE INDEX idx_address_address2_district_postal_code ON customer_data.address(address, address2) INCLUDE(district, postal_code, phone)");
this.dbFixture.AlterTargetDatabaseExecuteFullAndAllAlterScriptsAndCompare(DatabaseType.MicrosoftSql, sb.ToString(), port);
}

/// <summary>
/// Test migration script when target db doesn't have a trigger
/// </summary>
Expand Down
2 changes: 1 addition & 1 deletion SQLSchemaCompare/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "sqlschemacompare",
"version": "2024.12.1",
"version": "2025.3.1",
"license": "GPL-3.0-only",
"description": "The Swiss Army Knife of Database Schema Comparison for Microsoft SQL, mySQL and PostgreSQL which runs on Windows, Linux and macOS systems.",
"main": "app.js",
Expand Down
Loading