import java.util.*; /** * CWE-407 unit test for proxysql-0002: MySQL_FTS indexed_cols O(R*C*I) linear scan. * * Simulates ProxySQL's MySQL_FTS::index_table() inner loop: * for each of R rows, for each of C columns, std::find on indexed_cols vector of I entries. * Fix: replace vector with unordered_set for O(1) membership test. */ public class ProxySQLFTSIndexedColsTest { // --- DEFECTIVE: vector linear scan per column per row --- static long ftsIndexSlow(int rows, int cols, List indexedCols) { long ops = 0; for (int r = 0; r < rows; r++) { for (int c = 0; c < cols; c++) { String colName = "col_" + c; // std::find equivalent — O(I) scan for (String ic : indexedCols) { ops++; if (ic.equals(colName)) break; } } } return ops; } // --- PATCHED: unordered_set O(1) lookup per column per row --- static long ftsIndexFast(int rows, int cols, Set indexedColsSet) { long ops = 0; for (int r = 0; r < rows; r++) { for (int c = 0; c < cols; c++) { String colName = "col_" + c; ops++; // O(1) hash lookup boolean found = indexedColsSet.contains(colName); } } return ops; } public static void main(String[] args) { System.out.println("proxysql-0002: MySQL_FTS indexed_cols O(R*C*I) benchmark"); System.out.println("=".repeat(80)); int failures = 0; int total = 0; // R=5000 rows, C=20 columns, I=10 indexed columns (half of cols are indexed) int ROWS = 5000; int COLS = 20; int INDEXED = 10; // Build indexed_cols: first INDEXED column names List indexedColsList = new ArrayList<>(); Set indexedColsSet = new HashSet<>(); for (int i = 0; i < INDEXED; i++) { String name = "col_" + i; indexedColsList.add(name); indexedColsSet.add(name); } long[] slowOps = new long[1], fastOps = new long[1]; Runnable slow = () -> slowOps[0] = ftsIndexSlow(ROWS, COLS, indexedColsList); Runnable fast = () -> fastOps[0] = ftsIndexFast(ROWS, COLS, indexedColsSet); // Warmup slow.run(); fast.run(); long t0 = System.nanoTime(); slow.run(); long sMs = (System.nanoTime() - t0) / 1_000_000; long t1 = System.nanoTime(); fast.run(); long fMs = (System.nanoTime() - t1) / 1_000_000; double speedup = fastOps[0] > 0 ? (double) slowOps[0] / fastOps[0] : 0; System.out.printf(" %-58s slow:%4dms (%,d ops) fast:%4dms (%,d ops) speedup:%.1fx%n", "proxysql-0002 FTS indexed_cols O(R*C*I) vs O(R*C)", sMs, slowOps[0], fMs, fastOps[0], speedup); total++; // slow: R * C * I (worst case each col scans all I); fast: R * C // At R=5000, C=20, I=10: slow=1,000,000 ops, fast=100,000 ops -> 10x ratio boolean pass = slowOps[0] > fastOps[0] * 5L; if (!pass) { System.out.printf(" FAIL proxysql-0002: slowOps=%,d fastOps=%,d (expected >5x)%n", slowOps[0], fastOps[0]); failures++; } else { System.out.println(" PASS proxysql-0002"); } System.out.println("=".repeat(80)); System.out.printf("%d/%d %s%n", total - failures, total, failures == 0 ? "PASS" : "FAIL"); if (failures > 0) System.exit(1); } }