Skip to content

Commit

Permalink
Make filtered unique index update dependencies soft so they can be br…
Browse files Browse the repository at this point in the history
…oken if there's a cycle, since they might not be enforced in the database.

Fixes #28065
  • Loading branch information
AndriySvyryd authored Aug 31, 2022
1 parent 445efb3 commit 98c9381
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,7 @@ public static void SetDatabaseName(this IMutableIndex index, string? name)
/// <param name="index">The index.</param>
/// <returns>The index filter expression.</returns>
public static string? GetFilter(this IReadOnlyIndex index)
=> (index is RuntimeIndex)
? throw new InvalidOperationException(CoreStrings.RuntimeModelMissingData)
: (string?)index.FindAnnotation(RelationalAnnotationNames.Filter)?.Value;
=> (string?)index.FindAnnotation(RelationalAnnotationNames.Filter)?.Value;

/// <summary>
/// Returns the index filter expression.
Expand All @@ -120,11 +118,6 @@ public static void SetDatabaseName(this IMutableIndex index, string? name)
/// <returns>The index filter expression.</returns>
public static string? GetFilter(this IReadOnlyIndex index, in StoreObjectIdentifier storeObject)
{
if (index is RuntimeIndex)
{
throw new InvalidOperationException(CoreStrings.RuntimeModelMissingData);
}

var annotation = index.FindAnnotation(RelationalAnnotationNames.Filter);
if (annotation != null)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,10 @@ protected override void ProcessIndexAnnotations(
{
base.ProcessIndexAnnotations(annotations, index, runtimeIndex, runtime);

annotations.Remove(runtime ? RelationalAnnotationNames.TableIndexMappings : RelationalAnnotationNames.Filter);
if (runtime)
{
annotations.Remove(RelationalAnnotationNames.TableIndexMappings);
}
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,11 @@ public virtual IReadOnlyList<List<IReadOnlyModificationCommand>> TopologicalSort

AddSameTableEdges(_modificationCommandGraph);

return _modificationCommandGraph.BatchingTopologicalSort(static (_, _, edges) => edges.All(e => e is ITable), FormatCycle);
return _modificationCommandGraph.BatchingTopologicalSort(
static (_, _, edges) => edges.All(e =>
e is ITable
|| (e is ITableIndex index && index.Filter != null)),
FormatCycle);
}

private string FormatCycle(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1870,9 +1870,7 @@ public static void CreateAnnotations(RuntimeEntityType runtimeEntityType)
Assert.Equal("AlternateIndex", alternateIndex.Name);
Assert.Equal("AIX", alternateIndex.GetDatabaseName());
Assert.Null(alternateIndex[RelationalAnnotationNames.Filter]);
Assert.Equal(
CoreStrings.RuntimeModelMissingData,
Assert.Throws<InvalidOperationException>(() => alternateIndex.GetFilter()).Message);
Assert.Null(alternateIndex.GetFilter());
Assert.Equal(new[] { compositeIndex, alternateIndex }, principalAlternateId.GetContainingIndexes());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,45 @@ public virtual void Save_with_shared_foreign_key()
});
}

[ConditionalFact]
public virtual void Swap_filtered_unique_index_values()
{
var productId1 = new Guid("984ade3c-2f7b-4651-a351-642e92ab7146");
var productId2 = new Guid("0edc9136-7eed-463b-9b97-bdb9648ab877");

ExecuteWithStrategyInTransaction(
context =>
{
var product1 = context.Products.Find(productId1)!;
var product2 = context.Products.Find(productId2)!;
product2.Name = null;
product2.Price = product1.Price;
context.SaveChanges();
},
context =>
{
var product1 = context.Products.Find(productId1)!;
var product2 = context.Products.Find(productId2)!;
product2.Name = product1.Name;
product1.Name = null;
context.SaveChanges();
},
context =>
{
var product1 = context.Products.Find(productId1)!;
var product2 = context.Products.Find(productId2)!;
Assert.Equal(1.49M, product1.Price);
Assert.Null(product1.Name);
Assert.Equal(1.49M, product2.Price);
Assert.Equal("Apple Cider", product2.Name);
});
}

[ConditionalFact]
public abstract void Identifiers_are_generated_correctly();

Expand All @@ -133,6 +172,8 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con
modelBuilder.Entity<ProductTableWithView>().HasBaseType((string)null).ToView("ProductView").ToTable("ProductTable");
modelBuilder.Entity<ProductTableView>().HasBaseType((string)null).ToView("ProductTable");

modelBuilder.Entity<Product>().HasIndex(p => new { p.Name, p.Price }).IsUnique().HasFilter("Name IS NOT NULL");

modelBuilder
.Entity<
LoginEntityTypeWithAnExtremelyLongAndOverlyConvolutedNameThatIsUsedToVerifyThatTheStoreIdentifierGenerationLengthLimitIsWorkingCorrectlyDetails
Expand Down

0 comments on commit 98c9381

Please sign in to comment.