Skip to content

Commit b0f6417

Browse files
committed
RM-9647 added sorting of sql commands
1 parent 9f59305 commit b0f6417

28 files changed

Lines changed: 2014 additions & 139 deletions

Remotion/Data/DomainObjects.UnitTests/Factories/BaseConfiguration.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,8 @@ public static ITypeDiscoveryService GetTypeDiscoveryService (params Assembly[] r
8181
"Remotion.Data.DomainObjects.UnitTests.TestDomain",
8282
"Remotion.Data.DomainObjects.UnitTests.DataManagement.TestDomain",
8383
"Remotion.Data.DomainObjects.UnitTests.MixedDomains.TestDomain",
84-
"Remotion.Data.DomainObjects.UnitTests.Linq.TestDomain"
84+
"Remotion.Data.DomainObjects.UnitTests.Linq.TestDomain",
85+
"Remotion.Data.DomainObjects.UnitTests.Persistence.Rdbms.ForeignKeyOptimization"
8586
};
8687
return new FilteringTypeDiscoveryService(
8788
typeDiscoveryService,
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
// SPDX-FileCopyrightText: (c) RUBICON IT GmbH, www.rubicon.eu
2+
// SPDX-License-Identifier: LGPL-2.1-or-later
3+
using System;
4+
using System.Diagnostics;
5+
using System.Linq;
6+
using NUnit.Framework;
7+
using Remotion.Data.DomainObjects.Mapping;
8+
using Remotion.Data.DomainObjects.Persistence.Rdbms.ForeignKeyOptimization;
9+
using Remotion.Data.DomainObjects.Persistence.Rdbms.Model;
10+
using Remotion.Data.DomainObjects.UnitTests.Persistence.Rdbms.ForeignKeyOptimization.TestDomain;
11+
12+
namespace Remotion.Data.DomainObjects.UnitTests.Persistence.Rdbms.ForeignKeyOptimization;
13+
14+
[TestFixture]
15+
public class ForeignKeyOptimizationNodeFactoryTest : ForeignKeyOptimizationTestBase
16+
{
17+
private ForeignKeyOptimizationNodeFactory _nodeFactory;
18+
private MappingConfiguration _mappingConfiguration;
19+
20+
[SetUp]
21+
public void SetUp ()
22+
{
23+
_nodeFactory = new ForeignKeyOptimizationNodeFactory();
24+
_mappingConfiguration = GetMappingConfiguration();
25+
}
26+
27+
[Test]
28+
public void CreateNodes_ResultIsValid ()
29+
{
30+
var objectA = _mappingConfiguration.GetTypeDefinition(typeof(NodeFactoryObjectA));
31+
var objectB = _mappingConfiguration.GetTypeDefinition(typeof(NodeFactoryObjectB));
32+
var objectC = _mappingConfiguration.GetTypeDefinition(typeof(NodeFactoryObjectC));
33+
var objectD = _mappingConfiguration.GetTypeDefinition(typeof(NodeFactoryObjectDWithNoTable));
34+
35+
var nodes = _nodeFactory.CreateNodes([objectA, objectB, objectC, objectD]);
36+
37+
Assert.That(nodes.Count, Is.EqualTo(3));
38+
var aNode = nodes[0];
39+
var bNode = nodes[1];
40+
var cNode = nodes[2];
41+
42+
Assert.That(aNode.TableDefinition, Is.EqualTo(GetTableDefinition(objectA)));
43+
Assert.That(aNode.Edges.Count, Is.EqualTo(1));
44+
AssertEdge(aNode.Edges[0], ["FK_NodeFactoryObjectA_SelfCyclingPropID"], aNode, true);
45+
46+
Assert.That(bNode.TableDefinition, Is.EqualTo(GetTableDefinition(objectB)));
47+
Assert.That(bNode.TableDefinition, Is.EqualTo(GetTableDefinition(objectD)));
48+
Assert.That(bNode.Edges.Count, Is.EqualTo(2));
49+
AssertEdge(bNode.Edges[0], ["FK_NodeFactoryObjectB_NodeFactoryBPropAID", "FK_NodeFactoryObjectB_NodeFactoryDPropAID"], aNode, false);
50+
AssertEdge(bNode.Edges[1], ["FK_NodeFactoryObjectB_NodeFactoryDPropCID"], cNode, false);
51+
52+
Assert.That(cNode.TableDefinition, Is.EqualTo(GetTableDefinition(objectC)));
53+
Assert.That(cNode.Edges.Count, Is.EqualTo(2));
54+
55+
AssertEdge(cNode.Edges[0], ["FK_NodeFactoryObjectC_NodeFactoryCPropAID"], aNode, false);
56+
AssertEdge(cNode.Edges[1], ["FK_NodeFactoryObjectC_NodeFactoryCPropBID"], bNode, false);
57+
}
58+
59+
[Test]
60+
public void CreateNodes_WithMissingLink_ThrowsException ()
61+
{
62+
var objectA = _mappingConfiguration.GetTypeDefinition(typeof(NodeFactoryObjectA));
63+
var objectC = _mappingConfiguration.GetTypeDefinition(typeof(NodeFactoryObjectC));
64+
65+
Assert.That(
66+
() => _nodeFactory.CreateNodes([objectA, objectC]),
67+
Throws.InvalidOperationException.With.Message.EqualTo(
68+
"Could not find ForeignKeyOptimizationNode 'NodeFactoryObjectB' for foreign key FK_NodeFactoryObjectC_NodeFactoryCPropBID on node 'NodeFactoryObjectC'."));
69+
}
70+
71+
private void AssertEdge (ForeignKeyOptimizationEdge edgeToTest, string[] foreignKeyNames, ForeignKeyOptimizationNode pointingTo, bool isSelfCyclic)
72+
{
73+
Assert.That(edgeToTest.IsSelfCyclingEdge, Is.EqualTo(isSelfCyclic));
74+
Assert.That(edgeToTest.PointingTo, Is.EqualTo(pointingTo));
75+
76+
Assert.That(edgeToTest.ForeignKeys.Count, Is.EqualTo(foreignKeyNames.Length));
77+
Assert.That(edgeToTest.ForeignKeys.Select(k => k.ConstraintName), Is.EquivalentTo(foreignKeyNames));
78+
}
79+
80+
private TableDefinition GetTableDefinition (ClassDefinition classDefinition)
81+
{
82+
var entityDefinition = classDefinition.StorageEntityDefinition;
83+
while (true)
84+
{
85+
if (entityDefinition is TableDefinition tableDefinition)
86+
return tableDefinition;
87+
88+
if (entityDefinition is not FilterViewDefinition filterViewDefinition)
89+
{
90+
Assert.Fail($"Could not determine {nameof(TableDefinition)} for {classDefinition.ID}");
91+
throw new UnreachableException();
92+
}
93+
94+
entityDefinition = filterViewDefinition.BaseEntity;
95+
}
96+
}
97+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// SPDX-FileCopyrightText: (c) RUBICON IT GmbH, www.rubicon.eu
2+
// SPDX-License-Identifier: LGPL-2.1-or-later
3+
using System;
4+
using System.ComponentModel.Design;
5+
using System.Linq;
6+
using System.Reflection;
7+
using Remotion.Data.DomainObjects.Mapping;
8+
using Remotion.Data.DomainObjects.Persistence.Configuration;
9+
using Remotion.Data.DomainObjects.UnitTests.Factories;
10+
using Remotion.Data.DomainObjects.UnitTests.Persistence.Rdbms.ForeignKeyOptimization.TestDomain;
11+
using Remotion.Development.UnitTesting.Reflection.TypeDiscovery;
12+
using Remotion.Reflection.TypeDiscovery;
13+
using Remotion.Reflection.TypeDiscovery.AssemblyFinding;
14+
using Remotion.Reflection.TypeDiscovery.AssemblyLoading;
15+
using Remotion.ServiceLocation;
16+
17+
namespace Remotion.Data.DomainObjects.UnitTests.Persistence.Rdbms.ForeignKeyOptimization;
18+
19+
public abstract class ForeignKeyOptimizationTestBase
20+
{
21+
private static ITypeDiscoveryService GetTypeDiscoveryService (params Assembly[] rootAssemblies)
22+
{
23+
var rootAssemblyFinder = new FixedRootAssemblyFinder(rootAssemblies.Select(asm => new RootAssembly(asm, true)).ToArray());
24+
var assemblyLoader = new FilteringAssemblyLoader(ApplicationAssemblyLoaderFilter.Instance);
25+
var assemblyFinder = new CachingAssemblyFinderDecorator(new AssemblyFinder(rootAssemblyFinder, assemblyLoader));
26+
ITypeDiscoveryService typeDiscoveryService = new AssemblyFinderTypeDiscoveryService(assemblyFinder);
27+
28+
string[] whitelistedNamespaces = [typeof(ForeignKeyOptimizationDomainBase).Namespace];
29+
30+
return new FilteringTypeDiscoveryService(
31+
typeDiscoveryService,
32+
type =>
33+
{
34+
var @namespace = type.Namespace ?? string.Empty;
35+
return whitelistedNamespaces.Any(t => @namespace.StartsWith(t));
36+
});
37+
}
38+
39+
public MappingConfiguration GetMappingConfiguration ()
40+
{
41+
var storageSettings = SafeServiceLocator.Current.GetInstance<IStorageSettings>();
42+
var typeDiscoveryService = GetTypeDiscoveryService(GetType().Assembly);
43+
var mappingConfiguration = MappingConfiguration.Create(
44+
MappingReflectorObjectMother.CreateMappingReflector(typeDiscoveryService),
45+
new PersistenceModelLoader(storageSettings));
46+
return mappingConfiguration;
47+
}
48+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
// SPDX-FileCopyrightText: (c) RUBICON IT GmbH, www.rubicon.eu
2+
// SPDX-License-Identifier: LGPL-2.1-or-later
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Diagnostics;
6+
using System.Linq;
7+
using Moq;
8+
using NUnit.Framework;
9+
using Remotion.Data.DomainObjects.Mapping;
10+
using Remotion.Data.DomainObjects.Persistence.Rdbms.ForeignKeyOptimization;
11+
using Remotion.Data.DomainObjects.Persistence.Rdbms.Model;
12+
using Remotion.Data.DomainObjects.UnitTests.Persistence.Rdbms.ForeignKeyOptimization.TestDomain;
13+
14+
namespace Remotion.Data.DomainObjects.UnitTests.Persistence.Rdbms.ForeignKeyOptimization;
15+
16+
[TestFixture]
17+
public class GraphBasedForeignKeyOptimizationProviderTest : ForeignKeyOptimizationTestBase
18+
{
19+
private Mock<IForeignKeyOptimizationNodeFactory> _nodeFactoryStrictMock;
20+
private IForeignKeyOptimizationNodeFactory _realNodeFactory;
21+
private MappingConfiguration _mappingConfiguration;
22+
23+
[SetUp]
24+
public void SetUp ()
25+
{
26+
_realNodeFactory = new ForeignKeyOptimizationNodeFactory();
27+
_nodeFactoryStrictMock = new Mock<IForeignKeyOptimizationNodeFactory>(MockBehavior.Strict);
28+
_mappingConfiguration = GetMappingConfiguration();
29+
}
30+
31+
[Test]
32+
public void CreatesCorrectOrder_WithoutAnyBreaks ()
33+
{
34+
var objectA = _mappingConfiguration.GetTypeDefinition(typeof(NoBreaksObjectA));
35+
var objectB = _mappingConfiguration.GetTypeDefinition(typeof(NoBreaksObjectB));
36+
var objectC = _mappingConfiguration.GetTypeDefinition(typeof(NoBreaksObjectC));
37+
38+
var provider = new GraphBasedForeignKeyOptimizationProvider(_realNodeFactory);
39+
provider.Initialize([objectC, objectB, objectA]);
40+
41+
AssertPositionAndCorrectOrder(
42+
provider,
43+
[
44+
(objectA, true),
45+
(objectC, true),
46+
(objectB, true)
47+
]
48+
);
49+
50+
AssertForeignKeyProperties(provider, objectA, []);
51+
AssertForeignKeyProperties(provider, objectB, [nameof(NoBreaksObjectB.NoBreakBPropA), nameof(NoBreaksObjectB.NoBreakBPropC)]);
52+
AssertForeignKeyProperties(provider, objectC, [nameof(NoBreaksObjectC.NoBreakCPropA)]);
53+
}
54+
55+
[Test]
56+
public void CreatesCorrectOrder_WithoutAutomaticBreaks_BreakIsDeterminedByTableName ()
57+
{
58+
var objectA = _mappingConfiguration.GetTypeDefinition(typeof(AutomaticBreaksTableNameObjectA));
59+
var objectB = _mappingConfiguration.GetTypeDefinition(typeof(AutomaticBreaksTableNameObjectB));
60+
var objectC = _mappingConfiguration.GetTypeDefinition(typeof(AutomaticBreaksTableNameObjectC));
61+
62+
var nodes = _realNodeFactory.CreateNodes([objectB, objectA, objectC]);
63+
64+
var nodeForA = GetNodeForClassDefinition(objectA, nodes);
65+
var nodeForB = GetNodeForClassDefinition(objectB, nodes);
66+
var nodeForC = GetNodeForClassDefinition(objectC, nodes);
67+
var expectedBrokenEdge = GetEdgeForProperty(nameof(AutomaticBreaksTableNameObjectA.AutomaticBreakTableNameBPropB), nodeForA);
68+
69+
SetupNodeFactoryMock(nodes);
70+
71+
Assert.That(nodeForA.HasBrokenEdges, Is.False);
72+
Assert.That(nodeForB.HasBrokenEdges, Is.False);
73+
Assert.That(nodeForC.HasBrokenEdges, Is.False);
74+
75+
var provider = new GraphBasedForeignKeyOptimizationProvider(_nodeFactoryStrictMock.Object);
76+
provider.Initialize([]);
77+
78+
Assert.That(nodeForA.HasBrokenEdges, Is.True);
79+
Assert.That(nodeForA.BrokenEdges.Count, Is.EqualTo(1));
80+
Assert.That(nodeForA.BrokenEdges.Contains(expectedBrokenEdge), Is.True);
81+
82+
Assert.That(nodeForB.HasBrokenEdges, Is.False);
83+
Assert.That(nodeForC.HasBrokenEdges, Is.False);
84+
85+
AssertPositionAndCorrectOrder(
86+
provider,
87+
[
88+
(objectA, false), // A is broken therefore first
89+
(objectC, true), // C has no other dependencies than A and A is broken therefore second
90+
(objectB, true) // B has dependency on C therefore third
91+
]
92+
);
93+
94+
AssertForeignKeyProperties(provider, objectA, [nameof(AutomaticBreaksTableNameObjectA.AutomaticBreakTableNameBPropB)]);
95+
AssertForeignKeyProperties(provider, objectB, [nameof(AutomaticBreaksTableNameObjectB.AutomaticBreakTableNameBPropC)]);
96+
AssertForeignKeyProperties(provider, objectC, [nameof(AutomaticBreaksTableNameObjectC.AutomaticBreakTableNameCPropA)]);
97+
}
98+
99+
[Test]
100+
public void CreatesCorrectOrder_WithoutAutomaticBreaks_BreakIsDeterminedByCountOfOccurence ()
101+
{
102+
var objectZ = _mappingConfiguration.GetTypeDefinition(typeof(AutomaticBreaksCountObjectZ));
103+
var objectA = _mappingConfiguration.GetTypeDefinition(typeof(AutomaticBreaksCountObjectA));
104+
var objectB = _mappingConfiguration.GetTypeDefinition(typeof(AutomaticBreaksCountObjectB));
105+
var objectC = _mappingConfiguration.GetTypeDefinition(typeof(AutomaticBreaksCountObjectC));
106+
107+
var nodes = _realNodeFactory.CreateNodes([objectZ, objectC, objectA, objectB]);
108+
109+
foreach(var node in nodes)
110+
node.CalculateIndirectCyclicDependencies();
111+
112+
var nodeForZ = GetNodeForClassDefinition(objectZ, nodes);
113+
var nodeForA = GetNodeForClassDefinition(objectA, nodes);
114+
var nodeForB = GetNodeForClassDefinition(objectB, nodes);
115+
var nodeForC = GetNodeForClassDefinition(objectC, nodes);
116+
117+
var expectedBrokenEdge1 = GetEdgeForProperty(nameof(AutomaticBreaksCountObjectZ.AutomaticBreakCountZPropA), nodeForZ);
118+
var expectedBrokenEdge2 = GetEdgeForProperty(nameof(AutomaticBreaksCountObjectZ.AutomaticBreakCountTPropB), nodeForZ);
119+
120+
SetupNodeFactoryMock(nodes);
121+
122+
Assert.That(nodeForA.HasBrokenEdges, Is.False);
123+
Assert.That(nodeForB.HasBrokenEdges, Is.False);
124+
Assert.That(nodeForC.HasBrokenEdges, Is.False);
125+
Assert.That(nodeForZ.HasBrokenEdges, Is.False);
126+
127+
var provider = new GraphBasedForeignKeyOptimizationProvider(_nodeFactoryStrictMock.Object);
128+
provider.Initialize([]);
129+
130+
Assert.That(nodeForA.HasBrokenEdges, Is.False);
131+
Assert.That(nodeForB.HasBrokenEdges, Is.False);
132+
Assert.That(nodeForC.HasBrokenEdges, Is.False);
133+
134+
Assert.That(nodeForZ.HasBrokenEdges, Is.True);
135+
Assert.That(nodeForZ.BrokenEdges.Count, Is.EqualTo(2));
136+
Assert.That(nodeForZ.BrokenEdges.Contains(expectedBrokenEdge1), Is.True);
137+
Assert.That(nodeForZ.BrokenEdges.Contains(expectedBrokenEdge2), Is.True);
138+
139+
AssertPositionAndCorrectOrder(
140+
provider,
141+
[
142+
(objectZ, false), // Z is broken therefore first
143+
(objectA, true), // A and C has no other dependencies than Z and then they are ordered by their table name
144+
(objectC, true), // therefore A is second and C is third
145+
(objectB, true) // B has dependency on C therefore third
146+
]
147+
);
148+
149+
AssertForeignKeyProperties(provider, objectA, [nameof(AutomaticBreaksCountObjectA.AutomaticBreakCountAPropZ)]);
150+
AssertForeignKeyProperties(provider, objectB, [nameof(AutomaticBreaksCountObjectB.AutomaticBreakCountBPropC)]);
151+
AssertForeignKeyProperties(provider, objectC, [nameof(AutomaticBreaksCountObjectC.AutomaticBreakCountCPropZ)]);
152+
AssertForeignKeyProperties(provider, objectZ, [nameof(AutomaticBreaksCountObjectZ.AutomaticBreakCountZPropA), nameof(AutomaticBreaksCountObjectZ.AutomaticBreakCountTPropB)]);
153+
}
154+
155+
private void AssertPositionAndCorrectOrder (GraphBasedForeignKeyOptimizationProvider provider, params (ClassDefinition classDefinition, bool isOrderedCorrect)[] expectedValues)
156+
{
157+
for (var i = 0; i < expectedValues.Length; i++)
158+
{
159+
var expectedValue = expectedValues[i];
160+
var tableDefinition = GetTableDefinition(expectedValue.classDefinition);
161+
Assert.That(provider.GetSortPosition(tableDefinition), Is.EqualTo(i));
162+
Assert.That(provider.HasBeenSortedCorrectly(tableDefinition), Is.EqualTo(expectedValue.isOrderedCorrect));
163+
}
164+
}
165+
166+
private void AssertForeignKeyProperties (GraphBasedForeignKeyOptimizationProvider provider, ClassDefinition classDefinition, string[] expectedProps)
167+
{
168+
var actualProps = provider.GetForeignKeyRelevantPropertyDefinitions(classDefinition);
169+
Assert.That(actualProps.Count, Is.EqualTo(expectedProps.Length));
170+
171+
var expectedPropertyDefinitions = expectedProps.Select(e => GetPropertyDefinition(classDefinition, e));
172+
Assert.That(actualProps, Is.EquivalentTo(expectedPropertyDefinitions));
173+
}
174+
175+
private ForeignKeyOptimizationNode GetNodeForClassDefinition (ClassDefinition classDefinition, IReadOnlyList<ForeignKeyOptimizationNode> nodes)
176+
{
177+
var tableDefinition = GetTableDefinition(classDefinition);
178+
return nodes.Single(n => n.TableDefinition == tableDefinition);
179+
}
180+
181+
private ForeignKeyOptimizationEdge GetEdgeForProperty (string propertyName, ForeignKeyOptimizationNode node)
182+
{
183+
return node.Edges.Single(e => e.ForeignKeys.Any(f => f.ReferencingColumns.Any(c => c.Name == propertyName + "ID")));
184+
}
185+
186+
private TableDefinition GetTableDefinition (ClassDefinition classDefinition)
187+
{
188+
var entityDefinition = classDefinition.StorageEntityDefinition;
189+
while (true)
190+
{
191+
if (entityDefinition is TableDefinition tableDefinition)
192+
return tableDefinition;
193+
194+
if (entityDefinition is not FilterViewDefinition filterViewDefinition)
195+
{
196+
Assert.Fail($"Could not determine {nameof(TableDefinition)} for {classDefinition.ID}");
197+
throw new UnreachableException();
198+
}
199+
200+
entityDefinition = filterViewDefinition.BaseEntity;
201+
}
202+
}
203+
204+
private PropertyDefinition GetPropertyDefinition (ClassDefinition classDefinition, string propertyName)
205+
{
206+
return classDefinition.GetPropertyDefinitions().First(p => p.PropertyName.EndsWith("." + propertyName));
207+
}
208+
209+
private void SetupNodeFactoryMock (IReadOnlyList<ForeignKeyOptimizationNode> nodes)
210+
{
211+
_nodeFactoryStrictMock.Setup(stub => stub.CreateNodes(It.IsAny<IReadOnlyList<ClassDefinition>>())).Returns(nodes);
212+
}
213+
}

0 commit comments

Comments
 (0)