java-topology/defects/efcore/unit/EfCoreAlgorithm.java
russell@unturf.com d4ed2dff91 ORM wave: 24 defects patched across 10 ORMs (157 sites, 62 ecosystems)
Hibernate (5 HIGH): addColumn/addReferencedColumn/addIndex ArrayList→LinkedHashSet (19x)
  FK second-pass LinkedHashSet, orderHierarchy LinkedHashSet
MyBatis (1 MEDIUM): sortConstructorMappings indexOf→HashMap (12x)
EF Core (2 HIGH + 1 MEDIUM): FindGenerationProperty HashSet (250x),
  AddPrincipals HashSet (250x), FK discovery HashSet (6x)
Diesel (3 MEDIUM): SQLite/MySQL row position()→BTreeMap (51x)
SQLAlchemy (2 HIGH): _values_bindparam Set (500x), evaluated_keys Set (500x)
Peewee (1 MEDIUM): _SortedFieldList.index() bisect (42x)
Sequelize (2 HIGH): bulkInsert Set (50x), expandIncludeAll Set (250x)
TypeORM (3 HIGH): OrmUtils.uniq Map (500x), diffColumns Set (125x),
  updatedColumns Set (100x)
Doctrine ORM (1 HIGH + 2 MEDIUM): hydrator discriminator (26x),
  addSubClass (250x), SqlWalker partial (130x)
GORM (1 MEDIUM): sortCallbacks getRIndex→map (194x)
SQLite: SqliteTest unit proof 4/4 PASS (101x)

Unit tests: all PASS — Hibernate/MyBatis/EfCore/Diesel/SQLAlchemy/Peewee/
  Sequelize/TypeORM/Doctrine/GORM
Whitepaper: 157 sites, 62 ecosystems; PDF 752K
2026-03-27 13:34:26 -04:00

197 lines
7.5 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package support;
import java.util.*;
/**
* EfCoreAlgorithm — algorithmic models of EF Core CWE-407 defects.
*
* Three defects modelled:
*
* efcore-0001: PropertyExtensions.FindGenerationProperty()
* BFS traversal of FK chains using List.Contains() for visited check.
* O(D²) where D = FK chain depth.
*
* efcore-0002: IReadOnlyProperty.AddPrincipals()
* Recursive traversal of principal chain using List.Contains().
* O(P²) where P = principal chain length.
*
* efcore-0003: ForeignKeyPropertyDiscoveryConvention
* IReadOnlyList.Contains() called per (key_property, fk_property) pair
* inside model-building loops.
* O(K × Kp × Fp) where K=keys, Kp=key props, Fp=FK props.
*/
public class EfCoreAlgorithm {
// -----------------------------------------------------------------------
// efcore-0001: FindGenerationProperty — BFS FK chain traversal
// -----------------------------------------------------------------------
public static class Result {
public final int defectiveOps;
public final int fixedOps;
public Result(int defectiveOps, int fixedOps) {
this.defectiveOps = defectiveOps;
this.fixedOps = fixedOps;
}
}
/**
* Defective: BFS traversal using List.Contains() — O(D²) visited check.
* @param chainDepth number of FK hops in the principal chain
* @return number of Contains() calls made
*/
public static int findGenerationPropertyDefective(int chainDepth) {
// Simulate: traversalList = new List<IProperty> { seed }
List<Integer> traversalList = new ArrayList<>();
traversalList.add(0);
int containsCalls = 0;
int index = 0;
while (index < traversalList.size()) {
int current = traversalList.get(index);
// Simulate: foreach FK { nextProperty = principalKey.Properties[...] }
// One principal hop per step in the chain
if (current < chainDepth - 1) {
int next = current + 1;
containsCalls++; // traversalList.Contains(next)
if (!traversalList.contains(next)) {
traversalList.add(next);
}
}
index++;
}
return containsCalls;
}
/**
* Fixed: BFS traversal using HashSet for O(1) visited check.
* @param chainDepth number of FK hops
* @return number of set-membership operations made (always 1 per step)
*/
public static int findGenerationPropertyFixed(int chainDepth) {
List<Integer> traversalList = new ArrayList<>();
Set<Integer> traversalSet = new HashSet<>();
traversalList.add(0);
traversalSet.add(0);
int addCalls = 0;
int index = 0;
while (index < traversalList.size()) {
int current = traversalList.get(index);
if (current < chainDepth - 1) {
int next = current + 1;
addCalls++; // traversalSet.add(next) = O(1)
if (traversalSet.add(next)) {
traversalList.add(next);
}
}
index++;
}
return addCalls;
}
// -----------------------------------------------------------------------
// efcore-0002: AddPrincipals — recursive principal chain dedup
// -----------------------------------------------------------------------
/**
* Defective: recursive traversal with List.Contains() — O(P²).
* @param chainLength number of principal hops
* @return total Contains() calls made across all recursion levels
*/
public static int addPrincipalsDefective(int chainLength) {
List<Integer> visited = new ArrayList<>();
visited.add(0);
int[] callCount = {0};
addPrincipalsRecDefective(0, chainLength, visited, callCount);
return callCount[0];
}
private static void addPrincipalsRecDefective(
int current, int chainLength, List<Integer> visited, int[] callCount) {
if (current >= chainLength - 1) return;
int principal = current + 1;
callCount[0]++; // visited.Contains(principal)
if (!visited.contains(principal)) {
visited.add(principal);
addPrincipalsRecDefective(principal, chainLength, visited, callCount);
}
}
/**
* Fixed: recursive traversal with HashSet — O(P).
* @param chainLength number of principal hops
* @return total HashSet.add() calls
*/
public static int addPrincipalsFixed(int chainLength) {
List<Integer> principals = new ArrayList<>();
Set<Integer> visited = new HashSet<>();
principals.add(0);
visited.add(0);
int[] callCount = {0};
addPrincipalsRecFixed(0, chainLength, principals, visited, callCount);
return callCount[0];
}
private static void addPrincipalsRecFixed(
int current, int chainLength, List<Integer> principals,
Set<Integer> visited, int[] callCount) {
if (current >= chainLength - 1) return;
int principal = current + 1;
callCount[0]++; // visited.add(principal) — O(1)
if (visited.add(principal)) {
principals.add(principal);
addPrincipalsRecFixed(principal, chainLength, principals, visited, callCount);
}
}
// -----------------------------------------------------------------------
// efcore-0003: ForeignKeyPropertyDiscovery — key subset check
// -----------------------------------------------------------------------
/**
* Defective: IReadOnlyList.Contains() per (key_prop, fk_prop) pair — O(K×Kp×Fp).
* @param numKeys number of keys to iterate
* @param keyPropCount properties per key
* @param fkPropCount properties in the FK (the list being searched)
* @return total Contains() calls made
*/
public static int fkDiscoveryDefective(int numKeys, int keyPropCount, int fkPropCount) {
// Simulate foreignKeyProperties as a List
List<Integer> foreignKeyProperties = new ArrayList<>();
for (int i = 0; i < fkPropCount; i++) foreignKeyProperties.add(i);
int containsCalls = 0;
for (int k = 0; k < numKeys; k++) {
for (int kp = 0; kp < keyPropCount; kp++) {
int prop = kp % fkPropCount;
containsCalls++; // foreignKeyProperties.Contains(prop) = O(Fp)
boolean found = foreignKeyProperties.contains(prop);
if (!found) break;
}
}
return containsCalls;
}
/**
* Fixed: build HashSet once per FK, O(1) per contains check — O(K×Kp + Fp).
*/
public static int fkDiscoveryFixed(int numKeys, int keyPropCount, int fkPropCount) {
List<Integer> foreignKeyProperties = new ArrayList<>();
for (int i = 0; i < fkPropCount; i++) foreignKeyProperties.add(i);
// Build the set once
Set<Integer> fkPropsSet = new HashSet<>(foreignKeyProperties);
int containsCalls = 0;
for (int k = 0; k < numKeys; k++) {
for (int kp = 0; kp < keyPropCount; kp++) {
int prop = kp % fkPropCount;
containsCalls++; // fkPropsSet.contains(prop) = O(1)
boolean found = fkPropsSet.contains(prop);
if (!found) break;
}
}
return containsCalls;
}
}