java-topology/tests/sql/postgresql-0002-0004.sql
russell@unturf.com 0a580b313d undefect. CWE-407 — 63 sites patched across 27 ecosystems
Authors: russell@unturf.com · brackishbert@gmail.com · foxhop.net · TimeHexOn.com

Patches, unit tests, benchmarks, whitepaper, and outreach briefs.
Public domain — no copyright claimed. Use freely.
2026-03-26 17:11:57 -04:00

334 lines
12 KiB
PL/PgSQL

-- postgresql-0002-0004.sql — Functional tests for CWE-407 fixes
--
-- Tests correctness of the three postgresql defect fixes:
-- 0002 preptlist.c:180,206,316 tlist_member in MERGE/RETURNING var collection
-- 0003 equivclass.c:1041 list_member in equivalence-class EC matching
-- 0004 analyzejoins.c:1914 list_member in join-elimination reltarget merge
--
-- Run with: psql -f postgresql-0002-0004.sql
-- or: psql -v ON_ERROR_STOP=1 -f postgresql-0002-0004.sql
--
-- All tests are idempotent. Schema is dropped and recreated at the top.
-- Requires PostgreSQL 15+ (MERGE support for 0002 tests).
\set ON_ERROR_STOP 1
BEGIN;
-- ── Setup ────────────────────────────────────────────────────────────────────
DROP SCHEMA IF EXISTS cwe407_test CASCADE;
CREATE SCHEMA cwe407_test;
SET search_path TO cwe407_test;
-- ═══════════════════════════════════════════════════════════════════════════
-- 0002 — preptlist.c: MERGE/UPDATE/RETURNING Var dedup
--
-- Tests that MERGE, UPDATE...RETURNING, and multi-source queries produce
-- correct results with many columns (exercising the tlist dedup code path).
-- ═══════════════════════════════════════════════════════════════════════════
-- Wide target table (many columns to stress the tlist_member dedup loop)
CREATE TABLE target_wide (
id int PRIMARY KEY,
c01 int, c02 int, c03 int, c04 int, c05 int,
c06 int, c07 int, c08 int, c09 int, c10 int,
c11 int, c12 int, c13 int, c14 int, c15 int,
c16 int, c17 int, c18 int, c19 int, c20 int
);
CREATE TABLE source_wide (
id int PRIMARY KEY,
c01 int, c02 int, c03 int, c04 int, c05 int,
c06 int, c07 int, c08 int, c09 int, c10 int,
c11 int, c12 int, c13 int, c14 int, c15 int,
c16 int, c17 int, c18 int, c19 int, c20 int
);
INSERT INTO target_wide
SELECT
g,
g*1, g*2, g*3, g*4, g*5,
g*6, g*7, g*8, g*9, g*10,
g*11, g*12, g*13, g*14, g*15,
g*16, g*17, g*18, g*19, g*20
FROM generate_series(1, 100) g;
INSERT INTO source_wide
SELECT
g,
g*10, g*20, g*30, g*40, g*50,
g*60, g*70, g*80, g*90, g*100,
g*110, g*120, g*130, g*140, g*150,
g*160, g*170, g*180, g*190, g*200
FROM generate_series(51, 150) g;
-- Test 0002-A: MERGE with UPDATE action referencing all columns
-- Exercises preptlist.c:180 (action targetList Var dedup)
MERGE INTO target_wide t
USING source_wide s ON t.id = s.id
WHEN MATCHED THEN
UPDATE SET
c01 = s.c01, c02 = s.c02, c03 = s.c03, c04 = s.c04, c05 = s.c05,
c06 = s.c06, c07 = s.c07, c08 = s.c08, c09 = s.c09, c10 = s.c10,
c11 = s.c11, c12 = s.c12, c13 = s.c13, c14 = s.c14, c15 = s.c15,
c16 = s.c16, c17 = s.c17, c18 = s.c18, c19 = s.c19, c20 = s.c20
WHEN NOT MATCHED THEN
INSERT (id, c01,c02,c03,c04,c05,c06,c07,c08,c09,c10,
c11,c12,c13,c14,c15,c16,c17,c18,c19,c20)
VALUES (s.id, s.c01,s.c02,s.c03,s.c04,s.c05,
s.c06,s.c07,s.c08,s.c09,s.c10,
s.c11,s.c12,s.c13,s.c14,s.c15,
s.c16,s.c17,s.c18,s.c19,s.c20);
-- Verify: matched rows (51..100) updated to source values; new rows (101..150) inserted
DO $$
DECLARE
matched_ok bool;
inserted_ok bool;
BEGIN
SELECT bool_and(c01 = id * 10) INTO matched_ok
FROM target_wide WHERE id BETWEEN 51 AND 100;
SELECT bool_and(c01 = id * 10) INTO inserted_ok
FROM target_wide WHERE id BETWEEN 101 AND 150;
IF NOT matched_ok THEN
RAISE EXCEPTION '0002-A: MERGE UPDATE produced wrong column values';
END IF;
IF NOT inserted_ok THEN
RAISE EXCEPTION '0002-A: MERGE INSERT produced wrong column values';
END IF;
END;
$$;
\echo '0002-A PASS: MERGE with wide table — correct values after MERGE'
-- Test 0002-B: MERGE with join condition Var dedup
-- Exercises preptlist.c:206 (mergeJoinCondition Var dedup)
CREATE TABLE join_source (
id int,
extra1 int,
extra2 int,
extra3 int
);
INSERT INTO join_source VALUES (1,10,20,30),(2,11,21,31),(999,99,99,99);
CREATE TABLE join_target (
id int PRIMARY KEY,
val int
);
INSERT INTO join_target VALUES (1,0),(2,0);
MERGE INTO join_target t
USING join_source s ON t.id = s.id AND s.extra1 > 5 AND s.extra2 > 10
WHEN MATCHED THEN
UPDATE SET val = s.extra3;
DO $$
BEGIN
IF (SELECT val FROM join_target WHERE id=1) <> 30 THEN
RAISE EXCEPTION '0002-B: MERGE join condition Var not resolved correctly';
END IF;
IF (SELECT val FROM join_target WHERE id=2) <> 31 THEN
RAISE EXCEPTION '0002-B: MERGE join condition Var not resolved correctly for id=2';
END IF;
END;
$$;
\echo '0002-B PASS: MERGE with join condition Vars — correct update'
-- Test 0002-C: UPDATE...RETURNING with many columns
-- Exercises preptlist.c:316 (RETURNING Var dedup)
CREATE TABLE ret_tgt (id int PRIMARY KEY, a int, b int, c int, d int, e int);
CREATE TABLE ret_src (id int PRIMARY KEY, a int, b int, c int, d int, e int);
INSERT INTO ret_tgt VALUES (1,1,1,1,1,1),(2,2,2,2,2,2),(3,3,3,3,3,3);
INSERT INTO ret_src VALUES (1,10,20,30,40,50),(2,11,21,31,41,51);
UPDATE ret_tgt t SET a=s.a, b=s.b, c=s.c, d=s.d, e=s.e
FROM ret_src s
WHERE t.id = s.id
RETURNING t.id, t.a, t.b, t.c, t.d, t.e, s.a AS src_a;
DO $$
DECLARE rows int;
BEGIN
SELECT count(*) INTO rows FROM (
UPDATE cwe407_test.ret_tgt t
SET a=s.a, b=s.b, c=s.c, d=s.d, e=s.e
FROM cwe407_test.ret_src s
WHERE t.id = s.id
RETURNING t.id, t.a, s.a AS src_a
) sub WHERE sub.a = sub.src_a;
IF rows <> 2 THEN
RAISE EXCEPTION '0002-C: RETURNING produced unexpected row count: %', rows;
END IF;
END;
$$;
\echo '0002-C PASS: UPDATE...RETURNING with multi-table Vars — correct output'
-- ═══════════════════════════════════════════════════════════════════════════
-- 0003 — equivclass.c: EC Var membership check
--
-- Tests that query plans using equivalence classes for ORDER BY / sort
-- produce correct results. Many equivalence-class members stress the
-- list_member loop at equivclass.c:1041.
-- ═══════════════════════════════════════════════════════════════════════════
CREATE TABLE ec_t1 (a int, b int, c int);
CREATE TABLE ec_t2 (a int, b int, d int);
CREATE TABLE ec_t3 (a int, b int, e int);
INSERT INTO ec_t1 SELECT g, g*2, g*3 FROM generate_series(1,50) g;
INSERT INTO ec_t2 SELECT g, g*2, g*4 FROM generate_series(1,50) g;
INSERT INTO ec_t3 SELECT g, g*2, g*5 FROM generate_series(1,50) g;
CREATE INDEX ON ec_t1(a,b);
CREATE INDEX ON ec_t2(a,b);
CREATE INDEX ON ec_t3(a,b);
-- Test 0003-A: Three-way join with EC sort — exercises find_em_expr_for_rel
-- The join creates equivalence classes {t1.a, t2.a, t3.a} and {t1.b, t2.b, t3.b}.
-- The ORDER BY on t1.a + c forces EC member lookup to check Var availability.
DO $$
DECLARE
cnt int;
prev_ab int := 0;
ok bool := true;
BEGIN
FOR cnt IN
SELECT t1.a + t1.b
FROM ec_t1 t1
JOIN ec_t2 t2 ON t1.a = t2.a AND t1.b = t2.b
JOIN ec_t3 t3 ON t1.a = t3.a AND t1.b = t3.b
WHERE t1.c > 0
ORDER BY t1.a, t1.b
LOOP
IF cnt < prev_ab THEN
ok := false;
EXIT;
END IF;
prev_ab := cnt;
END LOOP;
IF NOT ok THEN
RAISE EXCEPTION '0003-A: EC member sort produced out-of-order results';
END IF;
END;
$$;
\echo '0003-A PASS: Three-way EC join with ORDER BY — correct sorted results'
-- Test 0003-B: EC with expressions — forces non-Var EC member path
DO $$
DECLARE cnt int;
BEGIN
SELECT count(*) INTO cnt
FROM (
SELECT t1.a, t1.b, t1.a + t1.b AS sum_ab
FROM ec_t1 t1
JOIN ec_t2 t2 ON t1.a = t2.a
WHERE t1.a + t1.b > 10
ORDER BY t1.a + t1.b
) sub
WHERE sum_ab <= 10;
IF cnt > 0 THEN
RAISE EXCEPTION '0003-B: EC expression filter produced rows outside range';
END IF;
END;
$$;
\echo '0003-B PASS: EC with expression members — correct filter results'
-- ═══════════════════════════════════════════════════════════════════════════
-- 0004 — analyzejoins.c: join-elimination reltarget merge
--
-- Tests that self-join elimination produces correct query results.
-- remove_useless_joins() calls remove_self_join_rel() when a join can be
-- eliminated; the patched code merges reltarget->exprs.
-- ═══════════════════════════════════════════════════════════════════════════
CREATE TABLE je_t (
id int PRIMARY KEY,
k int NOT NULL,
v int,
w int,
x int
);
CREATE UNIQUE INDEX ON je_t(k);
INSERT INTO je_t SELECT g, g, g*2, g*3, g*4 FROM generate_series(1,100) g;
-- Test 0004-A: Self-join on unique key — should be eliminated
-- After elimination, toRemove's exprs must be correctly merged into toKeep.
DO $$
DECLARE
direct_cnt int;
selfjoin_cnt int;
BEGIN
SELECT count(*) INTO direct_cnt FROM je_t WHERE k > 50;
SELECT count(*) INTO selfjoin_cnt
FROM je_t t1
JOIN je_t t2 ON t1.k = t2.k -- eliminatable: unique key join
WHERE t1.k > 50;
IF direct_cnt <> selfjoin_cnt THEN
RAISE EXCEPTION '0004-A: self-join elimination changed row count: direct=% selfjoin=%',
direct_cnt, selfjoin_cnt;
END IF;
END;
$$;
\echo '0004-A PASS: Self-join elimination — row count correct'
-- Test 0004-B: Self-join with projection of columns from both sides
-- After elimination, columns from the eliminated side must still be available.
DO $$
DECLARE
mismatched int;
BEGIN
SELECT count(*) INTO mismatched
FROM (
SELECT t1.v AS v1, t2.w AS w2, t1.x AS x1, t2.v AS v2
FROM je_t t1
JOIN je_t t2 ON t1.k = t2.k
WHERE t1.id BETWEEN 1 AND 20
) sub
WHERE v1 <> v2 OR x1 <> id * 4;
IF mismatched > 0 THEN
RAISE EXCEPTION '0004-B: self-join elimination produced wrong column values: % rows mismatched',
mismatched;
END IF;
END;
$$;
\echo '0004-B PASS: Self-join column projection — correct values after elimination'
-- Test 0004-C: Self-join with WHERE clause referencing both sides
DO $$
DECLARE cnt int;
BEGIN
SELECT count(*) INTO cnt
FROM je_t t1
JOIN je_t t2 ON t1.k = t2.k
WHERE t1.v > 100 AND t2.w > 150
ORDER BY t1.id;
DECLARE direct_cnt int;
SELECT count(*) INTO direct_cnt
FROM je_t
WHERE v > 100 AND w > 150;
IF cnt <> direct_cnt THEN
RAISE EXCEPTION '0004-C: self-join WHERE filter mismatch: eliminated=% direct=%',
cnt, direct_cnt;
END IF;
END;
$$;
\echo '0004-C PASS: Self-join with dual-side WHERE — correct filter after elimination'
-- ── Teardown ─────────────────────────────────────────────────────────────────
ROLLBACK;
\echo ''
\echo 'All postgresql-0002/0003/0004 functional tests PASSED.'