U4-8998 umbracoRelation table needs unique index on columns: parentId, childId, relType

This commit is contained in:
Shannon
2017-02-10 15:38:53 +11:00
parent d27fee8a5b
commit 2b82520067
7 changed files with 103 additions and 11 deletions

View File

@@ -16,6 +16,7 @@ namespace Umbraco.Core.Models.Rdbms
[Column("parentId")]
[ForeignKey(typeof(NodeDto), Name = "FK_umbracoRelation_umbracoNode")]
[Index(IndexTypes.UniqueNonClustered, Name = "IX_umbracoRelation_parentChildType", ForColumns = "parentId,childId,relType")]
public int ParentId { get; set; }
[Column("childId")]

View File

@@ -14,14 +14,7 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenSixZero
public override void Up()
{
var dbIndexes = SqlSyntax.GetDefinedIndexes(Context.Database)
.Select(x => new DbIndexDefinition()
{
TableName = x.Item1,
IndexName = x.Item2,
ColumnName = x.Item3,
IsUnique = x.Item4
}).ToArray();
var dbIndexes = SqlSyntax.GetDefinedIndexesDefinitions(Context.Database);
//make sure it doesn't already exist
if (dbIndexes.Any(x => x.IndexName.InvariantEquals("IX_umbracoNodePath")) == false)

View File

@@ -0,0 +1,68 @@
using System;
using System.Linq;
using Umbraco.Core.Logging;
using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenSixZero
{
[Migration("7.6.0", 0, Constants.System.UmbracoMigrationName)]
public class AddIndexesToUmbracoRelation : MigrationBase
{
public AddIndexesToUmbracoRelation(ISqlSyntaxProvider sqlSyntax, ILogger logger)
: base(sqlSyntax, logger)
{ }
public override void Up()
{
//Ensure this executes in a defered block which will be done inside of the migration transaction
this.Execute.Code(database =>
{
var dbIndexes = SqlSyntax.GetDefinedIndexesDefinitions(database);
//make sure it doesn't already exist
if (dbIndexes.Any(x => x.IndexName.InvariantEquals("IX_umbracoRelation_parentChildType")) == false)
{
//We need to check if this index has corrupted data and then clear that data
var duplicates = database.Fetch<dynamic>("SELECT parentId,childId,relType FROM umbracoRelation GROUP BY parentId,childId,relType HAVING COUNT(*) > 1");
if (duplicates.Count > 0)
{
//need to fix this there cannot be duplicates so we'll take the latest entries, it's really not going to matter though
foreach (var duplicate in duplicates)
{
var ids = database.Fetch<int>("SELECT id FROM umbracoRelation WHERE parentId=@parentId AND childId=@childId AND relType=@relType ORDER BY datetime DESC",
new { parentId = duplicate.parentId, childId = duplicate.childId, relType = duplicate.relType });
if (ids.Count == 1)
{
//this is just a safety check, this should absolutely never happen
throw new InvalidOperationException("Duplicates were detected but could not be discovered");
}
//delete the others
ids = ids.Skip(0).ToList();
//iterate in groups of 2000 to avoid the max sql parameter limit
foreach (var idGroup in ids.InGroupsOf(2000))
{
database.Execute("DELETE FROM umbracoRelation WHERE id IN (@ids)", new { ids = idGroup });
}
}
}
}
return "";
});
Create.Index("IX_umbracoRelation_parentChildType").OnTable("umbracoRelation")
.OnColumn("parentId").Ascending()
.OnColumn("childId").Ascending()
.OnColumn("relType").Ascending()
.WithOptions()
.Unique();
}
public override void Down()
{
Delete.Index("IX_umbracoNodePath").OnTable("umbracoNode");
}
}
}

View File

@@ -1,7 +1,23 @@
namespace Umbraco.Core.Persistence.SqlSyntax
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
namespace Umbraco.Core.Persistence.SqlSyntax
{
internal static class SqlSyntaxProviderExtensions
{
public static IEnumerable<DbIndexDefinition> GetDefinedIndexesDefinitions(this ISqlSyntaxProvider sql, Database db)
{
return sql.GetDefinedIndexes(db)
.Select(x => new DbIndexDefinition()
{
TableName = x.Item1,
IndexName = x.Item2,
ColumnName = x.Item3,
IsUnique = x.Item4
}).ToArray();
}
/// <summary>
/// Returns the quotes tableName.columnName combo
/// </summary>

View File

@@ -215,7 +215,7 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.GetUnitOfWork(commit: true))
{
var repository = RepositoryFactory.CreateRelationRepository(uow);
var query = new Query<IRelation>().Where(x => x.ChildId == id || x.ParentId == id);
var query = new Query<IRelation>().Where(x => x.ParentId == id || x.ChildId == id);
return repository.GetByQuery(query);
}
}
@@ -230,7 +230,7 @@ namespace Umbraco.Core.Services
if (relationType == null) return Enumerable.Empty<IRelation>();
var relationRepo = RepositoryFactory.CreateRelationRepository(uow);
var query = new Query<IRelation>().Where(x => (x.ChildId == id || x.ParentId == id) && x.RelationTypeId == relationType.Id);
var query = new Query<IRelation>().Where(x => (x.ParentId == id || x.ChildId == id) && x.RelationTypeId == relationType.Id);
return relationRepo.GetByQuery(query);
}
}

View File

@@ -479,6 +479,7 @@
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenFourZero\AddUniqueIdPropertyTypeGroupColumn.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenFourZero\RemoveParentIdPropertyTypeGroupColumn.cs" />
<Compile Include="Persistence\Mappers\TaskTypeMapper.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenSixZero\AddIndexesToUmbracoRelation.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenSixZero\AddIndexToUmbracoNodePath.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenSixZero\AddRelationTypeUniqueIdColumn.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenSixZero\AddMacroUniqueIdColumn.cs" />

View File

@@ -121,6 +121,19 @@ WHERE (([umbracoNode].[nodeObjectType] = @0))) x)".Replace(Environment.NewLine,
Assert.AreEqual("CREATE UNIQUE NONCLUSTERED INDEX [IX_A] ON [TheTable] ([A])", createExpression.ToString());
}
[Test]
public void CreateIndexBuilder_SqlServer_Unique_CreatesUniqueNonClusteredIndex_Multi_Columnn()
{
var sqlSyntax = new SqlServerSyntaxProvider();
var createExpression = new CreateIndexExpression(DatabaseProviders.SqlServer, new[] { DatabaseProviders.SqlServer }, sqlSyntax)
{
Index = { Name = "IX_AB" }
};
var builder = new CreateIndexBuilder(createExpression);
builder.OnTable("TheTable").OnColumn("A").Ascending().OnColumn("B").Ascending().WithOptions().Unique();
Assert.AreEqual("CREATE UNIQUE NONCLUSTERED INDEX [IX_AB] ON [TheTable] ([A],[B])", createExpression.ToString());
}
[Test]
public void CreateIndexBuilder_SqlServer_Clustered_CreatesClusteredIndex()
{