cleanup: remove duplicate hibernate-0007/quarkus-0003 patches; correct camel/rabbitmq UNDF stamps
This commit is contained in:
parent
919d3f2a57
commit
651aaa7e7b
8 changed files with 6 additions and 210 deletions
|
|
@ -1,110 +0,0 @@
|
|||
# UNDF: (pending)
|
||||
# hibernate-0007: InFlightMetadataCollectorImpl.buildRecursiveOrderedFkSecondPasses — O(2^D) diamond + O(N²) list scan
|
||||
|
||||
## CWE-407 — Algorithmic Complexity: O(2^D) diamond re-traversal + O(N) List.contains in FK ordering
|
||||
|
||||
| Field | Value |
|
||||
|--------------|-------|
|
||||
| ID | hibernate-0007 |
|
||||
| Severity | HIGH |
|
||||
| Ecosystem | hibernate |
|
||||
| Package | hibernate-core |
|
||||
| File | `hibernate-core/src/main/java/org/hibernate/boot/internal/InFlightMetadataCollectorImpl.java` |
|
||||
| Lines | 1835–1853 |
|
||||
| Complexity | O(2^D) on diamond FK dependency graphs; O(N) dedup guard |
|
||||
| Hot path | Called during schema bootstrap: `processSecondPasses()` → FK ordering phase |
|
||||
|
||||
## Defect
|
||||
|
||||
`buildRecursiveOrderedFkSecondPasses` recursively traverses the FK dependency graph
|
||||
to produce a topologically-ordered list of `FkSecondPass` operations. It uses `startTable`
|
||||
as a cycle guard (skips re-entering the starting table), but has no guard for diamond
|
||||
re-traversal of intermediate shared tables:
|
||||
|
||||
```java
|
||||
// InFlightMetadataCollectorImpl.java:1835-1853 (DEFECT)
|
||||
private void buildRecursiveOrderedFkSecondPasses(
|
||||
List<FkSecondPass> orderedFkSecondPasses,
|
||||
Map<String, Set<FkSecondPass>> isADependencyOf,
|
||||
String startTable,
|
||||
String currentTable) {
|
||||
final Set<FkSecondPass> dependencies = isADependencyOf.get( currentTable );
|
||||
if ( dependencies != null ) {
|
||||
for ( var fkSecondPass : dependencies ) {
|
||||
final String dependentTable = fkSecondPass.getValue().getTable()...render();
|
||||
if ( dependentTable.compareTo( startTable ) != 0 ) {
|
||||
buildRecursiveOrderedFkSecondPasses( // recurse — only guards startTable cycle,
|
||||
orderedFkSecondPasses, isADependencyOf, startTable, dependentTable ); // NOT diamond
|
||||
}
|
||||
if ( !orderedFkSecondPasses.contains( fkSecondPass ) ) { // O(N) List.contains!
|
||||
orderedFkSecondPasses.add( 0, fkSecondPass );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Two distinct defects:
|
||||
|
||||
1. **Diamond re-traversal O(2^D):** On a diamond FK dependency graph
|
||||
(T1 depends on T2 and T3; both T2 and T3 depend on T4), T4 is visited twice, 2^D times
|
||||
at depth D. The `startTable` guard only prevents cycles back to T1, not intermediate diamonds.
|
||||
|
||||
2. **O(N) List.contains dedup guard:** `orderedFkSecondPasses.contains(fkSecondPass)` is an
|
||||
O(N) scan of the already-ordered list. With N FK passes and diamond re-traversal,
|
||||
total cost: O(2^D × N). Even without diamonds, N passes each potentially visiting N
|
||||
already-ordered entries: O(N²).
|
||||
|
||||
## Fix
|
||||
|
||||
Add a `Set<String> visited` parameter to track globally-visited tables; replace
|
||||
`List.contains` with a `LinkedHashSet` for O(1) dedup:
|
||||
|
||||
```java
|
||||
// Call site — line 1804-1806
|
||||
final LinkedHashSet<FkSecondPass> orderedFkSecondPasses = new LinkedHashSet<>( fkSecondPassList.size() );
|
||||
for ( String tableName : isADependencyOf.keySet() ) {
|
||||
buildRecursiveOrderedFkSecondPasses( orderedFkSecondPasses, isADependencyOf, tableName, tableName, new HashSet<>() );
|
||||
}
|
||||
// process the ordered passes (LinkedHashSet preserves insertion order)
|
||||
for ( var sp : orderedFkSecondPasses ) {
|
||||
sp.doSecondPass( getEntityBindingMap() );
|
||||
}
|
||||
|
||||
// AFTER — O(N+E) total
|
||||
private void buildRecursiveOrderedFkSecondPasses(
|
||||
LinkedHashSet<FkSecondPass> orderedFkSecondPasses, // O(1) add/contains
|
||||
Map<String, Set<FkSecondPass>> isADependencyOf,
|
||||
String startTable,
|
||||
String currentTable,
|
||||
Set<String> visited) { // diamond guard
|
||||
if ( !visited.add( currentTable ) ) {
|
||||
return; // already traversed this table in this pass
|
||||
}
|
||||
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, visited );
|
||||
}
|
||||
orderedFkSecondPasses.add( fkSecondPass ); // O(1) dedup via LinkedHashSet
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Note: `LinkedHashSet` preserves insertion order (same semantics as `add(0, ...)` reversed)
|
||||
and provides O(1) `add`/`contains`. The `add(0, ...)` pattern builds the list in reverse
|
||||
topological order; `LinkedHashSet` with final reversal achieves the same.
|
||||
|
||||
## Speedup
|
||||
|
||||
| Diamond depth (D), N=100 passes | Before (visits) | After (visits) | Speedup |
|
||||
|---------------------------------|----------------|----------------|---------|
|
||||
| 5 | 3,100 | 100 | 31× |
|
||||
| 10 | 102,300 | 100 | 1,023× |
|
||||
| 15 | 3,276,700 | 100 | 32,767× |
|
||||
|
||||
Growth before: O(2^D × N). Growth after: O(N).
|
||||
Loading…
Add table
Add a link
Reference in a new issue