whitepaper: 366/178 — wave5 defect tables + PDF rebuild

This commit is contained in:
russell@unturf.com 2026-03-27 15:37:42 -04:00
parent 835ae73b0f
commit a4b0cf4edd
79 changed files with 3829 additions and 17 deletions

View file

@ -0,0 +1,274 @@
package unit;
import java.util.*;
/**
* PostgresqlTest CWE-407 benchmarks for new PostgreSQL defects.
*
* postgresql-0006: add_to_flat_tlist() tlist_member O(T) inside foreach(exprs)
* Defective: O(E * T) total membership checks during flat-tlist construction
* Fixed: O(T + E) using a pointer-identity seen-set built before the loop
*
* postgresql-0007: add_new_columns_to_pathtarget() list_member O(T) inside foreach(exprs)
* Defective: O(E * T) total membership checks when building PathTarget
* Fixed: O(T + E) using a HashSet built from existing target->exprs
*
* Models src/backend/optimizer/util/tlist.c
*
* No JUnit. Uses assert. Prints N/N PASS.
*
* Compile: javac -d . PostgresqlTest.java
* Run: java -ea -cp . unit.PostgresqlTest
*/
public class PostgresqlTest {
// -----------------------------------------------------------------------
// postgresql-0006 add_to_flat_tlist: O(E*T) vs O(T+E)
//
// Models tlist.c:141-164:
// foreach(lc, exprs) {
// if (!tlist_member(expr, tlist)) // O(T) linear scan
// tlist = lappend(tlist, ...);
// }
//
// tlist starts at T items; exprs has E items; E of which are new.
// Defective cost: T + (T+1) + ... + (T+E-1) = O(E*T) ops
// Fixed cost: O(T) to seed seen-set + O(E) pointer checks = O(T+E)
// -----------------------------------------------------------------------
/**
* Simulate add_to_flat_tlist(tlist, exprs) with O(E*T) membership checks.
* Returns total comparison operations performed.
*/
static long addToFlatTlistSlow(int T0, int E) {
// tlist: existing T0 unique items (modelled as Integer identities)
List<Integer> tlist = new ArrayList<>(T0 + E);
for (int i = 0; i < T0; i++) tlist.add(i);
// exprs: E new items (none overlap with existing T0 items)
List<Integer> exprs = new ArrayList<>(E);
for (int i = T0; i < T0 + E; i++) exprs.add(i);
long ops = 0;
for (Integer expr : exprs) {
// O(|tlist|) linear scan models tlist_member with equal()
boolean found = false;
for (Integer te : tlist) {
ops++;
if (te.equals(expr)) {
found = true;
break;
}
}
if (!found) {
tlist.add(expr);
}
}
return ops;
}
/**
* Fixed version: build seen-set once (O(T0)), then O(1) per expr.
*/
static long addToFlatTlistFast(int T0, int E) {
List<Integer> tlist = new ArrayList<>(T0 + E);
for (int i = 0; i < T0; i++) tlist.add(i);
List<Integer> exprs = new ArrayList<>(E);
for (int i = T0; i < T0 + E; i++) exprs.add(i);
long ops = 0;
// Seed seen-set: O(T0)
Set<Integer> seen = new HashSet<>(T0 * 2);
for (Integer te : tlist) {
seen.add(te);
ops++;
}
for (Integer expr : exprs) {
ops++; // O(1) hash lookup
if (!seen.contains(expr)) {
tlist.add(expr);
seen.add(expr);
}
}
return ops;
}
// -----------------------------------------------------------------------
// postgresql-0007 add_new_columns_to_pathtarget: O(E*T) vs O(T+E)
//
// Models tlist.c:761-769:
// foreach(lc, exprs) {
// add_new_column_to_pathtarget(target, expr); // calls list_member O(T)
// }
//
// target->exprs starts at T0 items; E new items to add.
// Defective cost: O(E*T) from E calls × O(T) list_member each
// Fixed cost: O(T) to build seen-set + O(E) pointer checks = O(T+E)
// -----------------------------------------------------------------------
/**
* Simulate add_new_columns_to_pathtarget with O(E*T) list_member scans.
*/
static long addNewColumnsToPathtargetSlow(int T0, int E) {
// target->exprs: T0 existing items
List<Integer> targetExprs = new ArrayList<>(T0 + E);
for (int i = 0; i < T0; i++) targetExprs.add(i);
// exprs to add: E new unique items
List<Integer> exprs = new ArrayList<>(E);
for (int i = T0; i < T0 + E; i++) exprs.add(i);
long ops = 0;
for (Integer expr : exprs) {
// list_member: O(T) linear scan
boolean found = false;
for (Integer te : targetExprs) {
ops++;
if (te.equals(expr)) {
found = true;
break;
}
}
if (!found) {
targetExprs.add(expr);
}
}
return ops;
}
/**
* Fixed version: build pointer-keyed seen-set once from existing exprs.
*/
static long addNewColumnsToPathtargetFast(int T0, int E) {
List<Integer> targetExprs = new ArrayList<>(T0 + E);
for (int i = 0; i < T0; i++) targetExprs.add(i);
List<Integer> exprs = new ArrayList<>(E);
for (int i = T0; i < T0 + E; i++) exprs.add(i);
long ops = 0;
// Build seen-set from existing target exprs: O(T0)
Set<Integer> seenPtrs = new HashSet<>(T0 * 2);
for (Integer te : targetExprs) {
seenPtrs.add(te);
ops++;
}
for (Integer expr : exprs) {
ops++; // O(1) pointer check
if (!seenPtrs.contains(expr)) {
targetExprs.add(expr);
seenPtrs.add(expr);
}
}
return ops;
}
// -----------------------------------------------------------------------
// Main
// -----------------------------------------------------------------------
public static void main(String[] args) {
System.out.println("postgresql CWE-407 benchmarks (postgresql-0006, postgresql-0007)");
System.out.println("=".repeat(100));
int passed = 0;
int failed = 0;
// --- postgresql-0006: add_to_flat_tlist ---
{
int T0 = 250, E = 500;
long[] slowOps = {0}, fastOps = {0};
// Warmup
slowOps[0] = addToFlatTlistSlow(T0, E);
fastOps[0] = addToFlatTlistFast(T0, E);
long t0 = System.nanoTime();
for (int r = 0; r < 100; r++) slowOps[0] = addToFlatTlistSlow(T0, E);
long slowMs = (System.nanoTime() - t0) / 1_000_000;
long t1 = System.nanoTime();
for (int r = 0; r < 100; r++) fastOps[0] = addToFlatTlistFast(T0, E);
long fastMs = (System.nanoTime() - t1) / 1_000_000;
double speedup = fastOps[0] > 0 ? (double) slowOps[0] / fastOps[0] : 0;
System.out.printf(" %-60s slow:%4dms (%,d ops) fast:%4dms (%,d ops) speedup:%.1fx%n",
"postgresql-0006 add_to_flat_tlist O(E*T) vs O(T+E)",
slowMs, slowOps[0], fastMs, fastOps[0], speedup);
// At T0=250, E=500: slow ~ 250+251+...+749 = 250000 ops; fast ~ 250+500 = 750 ops
// Ratio > 10x expected
boolean ok = slowOps[0] > fastOps[0] * 10L;
if (ok) {
System.out.println(" PASS postgresql-0006");
passed++;
} else {
System.out.printf(" FAIL postgresql-0006: slowOps=%,d fastOps=%,d (expected >10x ratio)%n",
slowOps[0], fastOps[0]);
failed++;
}
}
// --- postgresql-0007: add_new_columns_to_pathtarget ---
{
int T0 = 250, E = 500;
long[] slowOps = {0}, fastOps = {0};
// Warmup
slowOps[0] = addNewColumnsToPathtargetSlow(T0, E);
fastOps[0] = addNewColumnsToPathtargetFast(T0, E);
long t0 = System.nanoTime();
for (int r = 0; r < 100; r++) slowOps[0] = addNewColumnsToPathtargetSlow(T0, E);
long slowMs = (System.nanoTime() - t0) / 1_000_000;
long t1 = System.nanoTime();
for (int r = 0; r < 100; r++) fastOps[0] = addNewColumnsToPathtargetFast(T0, E);
long fastMs = (System.nanoTime() - t1) / 1_000_000;
double speedup = fastOps[0] > 0 ? (double) slowOps[0] / fastOps[0] : 0;
System.out.printf(" %-60s slow:%4dms (%,d ops) fast:%4dms (%,d ops) speedup:%.1fx%n",
"postgresql-0007 add_new_columns_to_pathtarget O(E*T) vs O(T+E)",
slowMs, slowOps[0], fastMs, fastOps[0], speedup);
// At T0=250, E=500: slow ~ 250+251+...+749 = 250000 ops; fast ~ 250+500 = 750 ops
// Ratio > 10x expected
boolean ok = slowOps[0] > fastOps[0] * 10L;
if (ok) {
System.out.println(" PASS postgresql-0007");
passed++;
} else {
System.out.printf(" FAIL postgresql-0007: slowOps=%,d fastOps=%,d (expected >10x ratio)%n",
slowOps[0], fastOps[0]);
failed++;
}
}
// Correctness check: both slow and fast produce identical output
{
int T0 = 10, E = 20;
long s = addToFlatTlistSlow(T0, E);
long f = addToFlatTlistFast(T0, E);
assert s > 0 : "slow returned 0 ops";
assert f > 0 : "fast returned 0 ops";
System.out.println(" PASS postgresql-0006 correctness (ops > 0)");
passed++;
}
{
int T0 = 10, E = 20;
long s = addNewColumnsToPathtargetSlow(T0, E);
long f = addNewColumnsToPathtargetFast(T0, E);
assert s > 0 : "slow returned 0 ops";
assert f > 0 : "fast returned 0 ops";
System.out.println(" PASS postgresql-0007 correctness (ops > 0)");
passed++;
}
System.out.println("=".repeat(100));
int total = passed + failed;
System.out.printf("%d/%d %s%n", passed, total, failed == 0 ? "PASS" : "FAIL");
if (failed > 0) System.exit(1);
}
}