import java.util.*; /** * CWE-407 unit tests for DuckDB defects. * * duckdb-0001: src/planner/binder.cpp Binder::AddCorrelatedColumn() * std::find over correlated_columns vector -> O(C²) dedup. * Fix: shadow HashSet for O(1) Contains(). * * duckdb-0002: src/planner/subquery/has_correlated_expressions.cpp * outer loop over correlated_columns × inner std::find -> O(N×M). * Fix: use CorrelatedColumns::Contains() O(1). * * duckdb-0003: src/optimizer/build_probe_side_optimizer.cpp * ComputeOverlappingBindings: for each needle, std::find in haystack -> O(N×H). * Fix: unordered_set for haystack -> O(N+H). * * duckdb-0004: src/optimizer/deliminator.cpp * For each aggregate group, std::find in join_bindings -> O(G×J). * Fix: unordered_set for join_bindings -> O(G+J). */ public class DuckDBTest { // --- Simulate ColumnBinding --- static class ColumnBinding { final long tableIndex; final long columnIndex; ColumnBinding(long t, long c) { this.tableIndex = t; this.columnIndex = c; } @Override public boolean equals(Object o) { if (!(o instanceof ColumnBinding)) return false; ColumnBinding b = (ColumnBinding) o; return tableIndex == b.tableIndex && columnIndex == b.columnIndex; } @Override public int hashCode() { return Objects.hash(tableIndex, columnIndex); } } // ========================================================= // duckdb-0001: AddCorrelatedColumn O(C²) vs O(C) // ========================================================= static class CorrelatedColumnsVector { List cols = new ArrayList<>(); void addColumn(int binding) { if (!cols.contains(binding)) { cols.add(0, binding); } } int size() { return cols.size(); } } static class CorrelatedColumnsHashSet { List cols = new ArrayList<>(); Set bindingSet = new HashSet<>(); void addColumn(int binding) { if (!bindingSet.contains(binding)) { cols.add(0, binding); bindingSet.add(binding); } } int size() { return cols.size(); } } static long mergeCorrelatedVector(List other, CorrelatedColumnsVector dst) { long ops = 0; for (int b : other) { ops += dst.cols.size(); dst.addColumn(b); } return ops; } static long mergeCorrelatedHashSet(List other, CorrelatedColumnsHashSet dst) { long ops = 0; for (int b : other) { ops += 1; dst.addColumn(b); } return ops; } static boolean testDuckDB0001() { System.out.println("=== duckdb-0001: AddCorrelatedColumn O(C^2) vs O(C) ==="); int C = 400; List other = new ArrayList<>(); for (int i = 0; i < C; i++) other.add(i); CorrelatedColumnsVector vec = new CorrelatedColumnsVector(); CorrelatedColumnsHashSet hset = new CorrelatedColumnsHashSet(); long opsVec = mergeCorrelatedVector(other, vec); long opsHash = mergeCorrelatedHashSet(other, hset); double ratio = (double) opsVec / opsHash; System.out.printf(" C=%d vector_ops=%,d hashset_ops=%,d ratio=%.1fx%n", C, opsVec, opsHash, ratio); assert vec.size() == C && hset.size() == C : "size mismatch"; boolean pass = ratio > 10.0; System.out.println(" " + (pass ? "PASS" : "FAIL")); return pass; } // ========================================================= // duckdb-0002: HasCorrelatedExpressions O(N×M) vs O(N+M) // ========================================================= static long hasCorrelatedVector(List correlatedCols, List binderCols) { long ops = 0; for (int b : correlatedCols) { for (int bc : binderCols) { ops++; if (bc == b) break; } } return ops; } static long hasCorrelatedHashSet(List correlatedCols, List binderCols) { Set binderSet = new HashSet<>(binderCols); long ops = binderCols.size(); for (int b : correlatedCols) { ops += 1; binderSet.contains(b); } return ops; } static boolean testDuckDB0002() { System.out.println("=== duckdb-0002: HasCorrelatedExpressions O(N*M) vs O(N+M) ==="); int N = 200; List correlatedCols = new ArrayList<>(); List binderCols = new ArrayList<>(); for (int i = 0; i < N; i++) correlatedCols.add(i); for (int i = 0; i < N; i++) binderCols.add(i + N); // non-overlapping worst case long opsVec = hasCorrelatedVector(correlatedCols, binderCols); long opsHash = hasCorrelatedHashSet(correlatedCols, binderCols); double ratio = (double) opsVec / opsHash; System.out.printf(" N=M=%d vector_ops=%,d hashset_ops=%,d ratio=%.1fx%n", N, opsVec, opsHash, ratio); boolean pass = ratio > 10.0; System.out.println(" " + (pass ? "PASS" : "FAIL")); return pass; } // ========================================================= // duckdb-0003: ComputeOverlappingBindings O(N×H) vs O(N+H) // ========================================================= static long computeOverlappingDefective(List haystack, List needles) { long ops = 0; for (ColumnBinding needle : needles) { for (ColumnBinding h : haystack) { ops++; if (h.equals(needle)) break; } } return ops; } static long computeOverlappingFixed(List haystack, List needles) { Set haystackSet = new HashSet<>(haystack); long ops = haystack.size(); // build set for (ColumnBinding needle : needles) { ops++; haystackSet.contains(needle); } return ops; } static boolean testDuckDB0003() { System.out.println("=== duckdb-0003: ComputeOverlappingBindings O(N*H) vs O(N+H) ==="); int N = 500; List haystack = new ArrayList<>(); List needles = new ArrayList<>(); for (int i = 0; i < N; i++) { haystack.add(new ColumnBinding(0, i)); needles.add(new ColumnBinding(0, i + N / 2)); // half overlap, worst-case scan } long opsDefective = computeOverlappingDefective(haystack, needles); long opsFixed = computeOverlappingFixed(haystack, needles); double ratio = (double) opsDefective / opsFixed; System.out.printf(" N=H=%d defective_ops=%,d fixed_ops=%,d ratio=%.1fx%n", N, opsDefective, opsFixed, ratio); boolean pass = ratio > 10.0; System.out.println(" " + (pass ? "PASS" : "FAIL")); return pass; } // ========================================================= // duckdb-0004: Deliminator group-join binding check O(G×J) // ========================================================= static long checkGroupsDefective(List joinBindings, long groupIndex, int groupCount) { long ops = 0; for (int g = 0; g < groupCount; g++) { ColumnBinding target = new ColumnBinding(groupIndex, g); for (ColumnBinding jb : joinBindings) { ops++; if (jb.equals(target)) break; } } return ops; } static long checkGroupsFixed(List joinBindings, long groupIndex, int groupCount) { Set joinSet = new HashSet<>(joinBindings); long ops = joinBindings.size(); // build set for (int g = 0; g < groupCount; g++) { ops++; joinSet.contains(new ColumnBinding(groupIndex, g)); } return ops; } static boolean testDuckDB0004() { System.out.println("=== duckdb-0004: Deliminator group-join check O(G*J) vs O(G+J) ==="); int N = 500; List joinBindings = new ArrayList<>(); for (int i = 0; i < N; i++) joinBindings.add(new ColumnBinding(42, i)); long opsDefective = checkGroupsDefective(joinBindings, 42, N); long opsFixed = checkGroupsFixed(joinBindings, 42, N); double ratio = (double) opsDefective / opsFixed; System.out.printf(" G=J=%d defective_ops=%,d fixed_ops=%,d ratio=%.1fx%n", N, opsDefective, opsFixed, ratio); boolean pass = ratio > 10.0; System.out.println(" " + (pass ? "PASS" : "FAIL")); return pass; } // ========================================================= public static void main(String[] args) { int pass = 0, fail = 0; if (testDuckDB0001()) pass++; else fail++; if (testDuckDB0002()) pass++; else fail++; if (testDuckDB0003()) pass++; else fail++; if (testDuckDB0004()) pass++; else fail++; System.out.printf("%nDuckDB CWE-407: %d/%d PASS%n", pass, pass + fail); if (fail > 0) System.exit(1); } }