java-topology/defects/hibernate/patch/hibernate-0007-fk-second-pass-diamond-recursion.patch

39 lines
2.4 KiB
Diff

# UNDF: UNDF-2026-000000464
--- a/hibernate-core/src/main/java/org/hibernate/boot/internal/InFlightMetadataCollectorImpl.java
+++ b/hibernate-core/src/main/java/org/hibernate/boot/internal/InFlightMetadataCollectorImpl.java
@@ -1803,7 +1803,8 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector
// using the isADependencyOf map we order the FkSecondPass recursively instances into the right order for processing
final LinkedHashSet<FkSecondPass> orderedFkSecondPassesSet = new LinkedHashSet<>();
for ( String tableName : isADependencyOf.keySet() ) {
- buildRecursiveOrderedFkSecondPasses( orderedFkSecondPassesSet, isADependencyOf, tableName, tableName );
+ buildRecursiveOrderedFkSecondPasses( orderedFkSecondPassesSet, isADependencyOf, tableName, tableName,
+ new HashSet<>() );
}
// Reconstruct ordered list from set (order maintained by LinkedHashSet insertion sequence)
final List<FkSecondPass> orderedFkSecondPasses = new ArrayList<>( orderedFkSecondPassesSet );
@@ -1835,9 +1836,16 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector
private void buildRecursiveOrderedFkSecondPasses(
LinkedHashSet<FkSecondPass> orderedFkSecondPasses,
Map<String, Set<FkSecondPass>> isADependencyOf,
String startTable,
- String currentTable) {
+ String currentTable,
+ Set<String> visitedTables) {
+ // hibernate-0007 fix: guard against diamond-graph exponential re-visitation.
+ // The original startTable identity check only prevented direct self-cycles;
+ // it did not prevent intermediate diamond nodes from being visited O(2^D) times.
+ // Adding visitedTables reduces the recursion from O(2^D) to O(T+E).
+ if ( !visitedTables.add( currentTable ) ) {
+ return;
+ }
final Set<FkSecondPass> dependencies = isADependencyOf.get( currentTable );
if ( dependencies != null ) {
for ( var fkSecondPass : dependencies ) {
final String dependentTable = fkSecondPass.getValue().getTable().getQualifiedTableName().render();
if ( dependentTable.compareTo( startTable ) != 0 ) {
- buildRecursiveOrderedFkSecondPasses( orderedFkSecondPasses, isADependencyOf, startTable, dependentTable );
+ buildRecursiveOrderedFkSecondPasses( orderedFkSecondPasses, isADependencyOf, startTable,
+ dependentTable, visitedTables );
}
// O(1) with LinkedHashSet; was O(N) ArrayList.contains() + O(N) add(0,...)
orderedFkSecondPasses.add( fkSecondPass );