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
132 lines
5 KiB
Java
132 lines
5 KiB
Java
package support;
|
||
|
||
import java.util.*;
|
||
|
||
/**
|
||
* DieselAlgorithm — algorithmic models of Diesel (Rust) CWE-407 defects.
|
||
*
|
||
* Three defects modelled:
|
||
*
|
||
* diesel-0001: SqliteRow Duplicated RowIndex<&str>
|
||
* column_names.iter().position() — O(N_cols) per named-column access.
|
||
* Called once per deserialized field access on a duplicated row.
|
||
*
|
||
* diesel-0002: OwnedSqliteRow RowIndex<&str>
|
||
* Same pattern on the owned (long-lived) variant.
|
||
*
|
||
* diesel-0003: MysqlRow RowIndex<&str>
|
||
* metadata.fields().iter().enumerate().find() — O(N_cols) linear scan.
|
||
*/
|
||
public class DieselAlgorithm {
|
||
|
||
public static class Result {
|
||
public final int defectiveOps;
|
||
public final int fixedOps;
|
||
public Result(int defectiveOps, int fixedOps) {
|
||
this.defectiveOps = defectiveOps;
|
||
this.fixedOps = fixedOps;
|
||
}
|
||
}
|
||
|
||
// -----------------------------------------------------------------------
|
||
// diesel-0001/0002: SQLite Duplicated row — named column lookup
|
||
//
|
||
// Pattern: for each row in result set, for each named-column access,
|
||
// scan the column_names slice for a matching name.
|
||
//
|
||
// Cost: O(rows × cols_accessed × total_cols)
|
||
// Fixed: build index map once per statement; O(rows × cols_accessed × log(total_cols))
|
||
// -----------------------------------------------------------------------
|
||
|
||
/**
|
||
* Defective: linear scan for column name on every access.
|
||
*
|
||
* @param totalCols total columns in the result set (width of row)
|
||
* @param colsAccessed number of named-column accesses per row
|
||
* @param numRows number of rows in the result set
|
||
* @return total position-scan steps (cost metric)
|
||
*/
|
||
public static long sqliteRowDefective(int totalCols, int colsAccessed, int numRows) {
|
||
// Simulate column_names as a list (Rust: Rc<[Option<String>]>)
|
||
List<String> columnNames = new ArrayList<>();
|
||
for (int c = 0; c < totalCols; c++) {
|
||
columnNames.add("col_" + c);
|
||
}
|
||
|
||
long totalScanSteps = 0;
|
||
for (int r = 0; r < numRows; r++) {
|
||
for (int a = 0; a < colsAccessed; a++) {
|
||
String target = "col_" + (a % totalCols);
|
||
// Simulate: column_names.iter().position(|n| n == target)
|
||
for (int i = 0; i < columnNames.size(); i++) {
|
||
totalScanSteps++;
|
||
if (columnNames.get(i).equals(target)) break;
|
||
}
|
||
}
|
||
}
|
||
return totalScanSteps;
|
||
}
|
||
|
||
/**
|
||
* Fixed: build a Map<String, Integer> once per statement (once, not per row),
|
||
* then O(log N) lookup per access via TreeMap.
|
||
*
|
||
* @param totalCols total columns
|
||
* @param colsAccessed named-column accesses per row
|
||
* @param numRows rows in result set
|
||
* @return total lookup steps (cost metric)
|
||
*/
|
||
public static long sqliteRowFixed(int totalCols, int colsAccessed, int numRows) {
|
||
List<String> columnNames = new ArrayList<>();
|
||
for (int c = 0; c < totalCols; c++) {
|
||
columnNames.add("col_" + c);
|
||
}
|
||
|
||
// Build index map ONCE (per statement / per Duplicated construction)
|
||
Map<String, Integer> columnNameIndex = new TreeMap<>();
|
||
for (int i = 0; i < columnNames.size(); i++) {
|
||
columnNameIndex.put(columnNames.get(i), i);
|
||
}
|
||
|
||
long totalLookupOps = 0;
|
||
for (int r = 0; r < numRows; r++) {
|
||
for (int a = 0; a < colsAccessed; a++) {
|
||
String target = "col_" + (a % totalCols);
|
||
// O(log N) TreeMap lookup
|
||
totalLookupOps++;
|
||
columnNameIndex.get(target); // O(log totalCols)
|
||
}
|
||
}
|
||
return totalLookupOps;
|
||
}
|
||
|
||
// -----------------------------------------------------------------------
|
||
// diesel-0003: MySQL row — named column lookup
|
||
// Same linear scan pattern, same fix.
|
||
// -----------------------------------------------------------------------
|
||
|
||
/**
|
||
* Defective: metadata.fields().iter().enumerate().find() — O(N_cols) per access.
|
||
*/
|
||
public static long mysqlRowDefective(int totalCols, int colsAccessed, int numRows) {
|
||
return sqliteRowDefective(totalCols, colsAccessed, numRows);
|
||
}
|
||
|
||
/**
|
||
* Fixed: build BTreeMap once per MysqlRow construction.
|
||
*/
|
||
public static long mysqlRowFixed(int totalCols, int colsAccessed, int numRows) {
|
||
return sqliteRowFixed(totalCols, colsAccessed, numRows);
|
||
}
|
||
|
||
// -----------------------------------------------------------------------
|
||
// Complexity ratio helper
|
||
// -----------------------------------------------------------------------
|
||
|
||
/** Returns the ratio defective/fixed scan steps for a given configuration. */
|
||
public static double complexityRatio(int totalCols, int colsAccessed, int numRows) {
|
||
long def = sqliteRowDefective(totalCols, colsAccessed, numRows);
|
||
long fix = sqliteRowFixed(totalCols, colsAccessed, numRows);
|
||
return (double) def / fix;
|
||
}
|
||
}
|