package unit; import java.util.*; /** * CockroachDB CWE-407 unit tests — standalone, no JUnit. * * cockroachdb-0001 IndexesUsed.add() slices.Contains on growing slice * pkg/sql/opt/exec/execbuilder/builder.go:211 */ public class CockroachDBTest { // ----------------------------------------------------------------------- // cockroachdb-0001: IndexesUsed.add — slice contains vs map lookup // // Models adding N (tableID, indexID) pairs with deduplication. // In a complex query the same index may be referenced multiple times; // add() must skip duplicates. // Slow: scan the existing slice for each add — O(N) per add, O(N²) total. // Fast: maintain a HashSet of pairs alongside the list — O(1) per add. // ----------------------------------------------------------------------- /** Slow: list-based dedup (models slices.Contains). Returns total comparison ops. */ static long indexesUsedSlowOps(int numAdds, int numUnique) { // Each "index" is encoded as a long: tableID << 32 | indexID List indexes = new ArrayList<>(); long ops = 0; Random rng = new Random(42); for (int i = 0; i < numAdds; i++) { long key = (long)(rng.nextInt(numUnique / 10 + 1)) << 32 | (rng.nextInt(numUnique + 1)); // Linear scan of existing list (models slices.Contains) boolean found = false; for (int j = 0; j < indexes.size(); j++) { ops++; if (indexes.get(j).equals(key)) { found = true; break; } } if (!found) indexes.add(key); } return ops; } /** Fast: HashSet-based dedup. Returns total comparison ops (1 per add). */ static long indexesUsedFastOps(int numAdds, int numUnique) { List indexes = new ArrayList<>(); Set seen = new HashSet<>(); long ops = 0; Random rng = new Random(42); for (int i = 0; i < numAdds; i++) { long key = (long)(rng.nextInt(numUnique / 10 + 1)) << 32 | (rng.nextInt(numUnique + 1)); ops++; // O(1) set lookup if (seen.add(key)) { indexes.add(key); } } return ops; } // ----------------------------------------------------------------------- // Test runner // ----------------------------------------------------------------------- public static void main(String[] args) { int passed = 0; int failed = 0; // N = number of add() calls (index references per query build) int[] sizes = {50, 100, 200, 500}; for (int n : sizes) { long slow = indexesUsedSlowOps(n, n); long fast = indexesUsedFastOps(n, n); // At N=50+, slow grows quadratically, fast is linear boolean pass = slow > fast * 5; System.out.printf("cockroachdb-0001 N=%-4d slow=%6d fast=%4d ratio=%5.1fx %s%n", n, slow, fast, (double) slow / fast, pass ? "PASS" : "FAIL"); if (pass) passed++; else failed++; } System.out.printf("%nTotal: %d/%d PASS%n", passed, passed + failed); if (failed > 0) System.exit(1); } }