package unit; import java.util.*; import java.util.stream.*; /** * Standalone unit test for starrocks-0001: * MaterializedViewRewriter.getTableToRelationid — List.contains() in column-ref loop → O(N×T). * * Simulates getTableToRelationid(): * for each column-ref → table entry, test membership in tableList. * * Compile: javac -d . MvTableRelationAlgorithm.java * Run: java unit.MvTableRelationAlgorithm */ public class MvTableRelationAlgorithm { // ── Minimal stubs ──────────────────────────────────────────────────────── static class Table { final String name; Table(String name) { this.name = name; } @Override public boolean equals(Object o) { return o instanceof Table && name.equals(((Table) o).name); } @Override public int hashCode() { return name.hashCode(); } @Override public String toString() { return name; } } static class ColRef { final int id; ColRef(int id) { this.id = id; } @Override public int hashCode() { return id; } @Override public boolean equals(Object o) { return o instanceof ColRef && id == ((ColRef) o).id; } } // ── Result ─────────────────────────────────────────────────────────────── static class Result { final Map> tableToRelationId; final long ns; Result(Map> m, long ns) { this.tableToRelationId = m; this.ns = ns; } } // ── Defective: List.contains() O(T) per column ref ──────────────── static class DefectiveAlgorithm { Result getTableToRelationid( Map colRefToTable, Set validColumnRefs, List
tableList) { // List → O(T) .contains Map> result = new HashMap<>(); long t0 = System.nanoTime(); for (Map.Entry entry : colRefToTable.entrySet()) { if (!tableList.contains(entry.getValue())) { // CWE-407 site continue; } if (!validColumnRefs.contains(entry.getKey())) { continue; } result.computeIfAbsent(entry.getValue(), k -> new HashSet<>()) .add(entry.getKey().id % 100); // synthetic relation ID } return new Result(result, System.nanoTime() - t0); } } // ── Fixed: Set
.contains() O(1) ──────────────────────────────────── static class FixedAlgorithm { Result getTableToRelationid( Map colRefToTable, Set validColumnRefs, List
tableList) { Map> result = new HashMap<>(); long t0 = System.nanoTime(); Set
tableSet = new HashSet<>(tableList); // O(T) once for (Map.Entry entry : colRefToTable.entrySet()) { if (!tableSet.contains(entry.getValue())) { // O(1) continue; } if (!validColumnRefs.contains(entry.getKey())) { continue; } result.computeIfAbsent(entry.getValue(), k -> new HashSet<>()) .add(entry.getKey().id % 100); } return new Result(result, System.nanoTime() - t0); } } // ── Test data helpers ──────────────────────────────────────────────────── static Table[] makeTables(int T) { Table[] tables = new Table[T]; for (int i = 0; i < T; i++) tables[i] = new Table("tbl_" + i); return tables; } static Map makeColRefMap(int N, Table[] tables) { Map m = new LinkedHashMap<>(); for (int i = 0; i < N; i++) { m.put(new ColRef(i), tables[i % tables.length]); } return m; } // ── Assertions ─────────────────────────────────────────────────────────── static void assertEquals(Object expected, Object actual, String msg) { if (!expected.equals(actual)) throw new AssertionError(msg + ": expected=" + expected + " actual=" + actual); } static void assertTrue(boolean cond, String msg) { if (!cond) throw new AssertionError(msg); } public static void main(String[] args) { System.out.println("=== starrocks-0001: MvTableRelationAlgorithm ==="); DefectiveAlgorithm defAlg = new DefectiveAlgorithm(); FixedAlgorithm fixAlg = new FixedAlgorithm(); // Small correctness test { int N = 50, T = 5; Table[] tables = makeTables(T); Map colMap = makeColRefMap(N, tables); Set valid = new HashSet<>(colMap.keySet()); List
tableList = Arrays.asList(tables).subList(0, 3); Result dr = defAlg.getTableToRelationid(colMap, valid, new ArrayList<>(tableList)); Result fr = fixAlg.getTableToRelationid(colMap, valid, new ArrayList<>(tableList)); assertEquals(dr.tableToRelationId, fr.tableToRelationId, "tableToRelationId contents"); System.out.println("PASS correctness (N=50, T=5)"); } // Benchmark { int N = 10000, T = 200; Table[] tables = makeTables(T); Map colMap = makeColRefMap(N, tables); Set valid = new HashSet<>(colMap.keySet()); // tableList covers only first 100 tables — forces full scan of list per entry List
tableList = Arrays.asList(tables).subList(0, 100); // warm up for (int i = 0; i < 5; i++) { defAlg.getTableToRelationid(colMap, valid, new ArrayList<>(tableList)); fixAlg.getTableToRelationid(colMap, valid, new ArrayList<>(tableList)); } long defNs = 0, fixNs = 0; int reps = 30; for (int i = 0; i < reps; i++) { defNs += defAlg.getTableToRelationid(colMap, valid, new ArrayList<>(tableList)).ns; fixNs += fixAlg.getTableToRelationid(colMap, valid, new ArrayList<>(tableList)).ns; } defNs /= reps; fixNs /= reps; double ratio = (double) defNs / Math.max(1, fixNs); System.out.printf("BENCH N=%d T=%d (list size=%d) reps=%d%n", N, T, tableList.size(), reps); System.out.printf(" defective avg: %,d ns%n", defNs); System.out.printf(" fixed avg: %,d ns%n", fixNs); System.out.printf(" speedup: %.1fx%n", ratio); assertTrue(ratio >= 2.0, "Expected fixed >= 2x faster, got " + ratio + "x"); System.out.println("PASS speedup >= 2x"); } System.out.println("=== ALL PASS ==="); } }