From 5e8ccf099d0a524db5459f9f2c62ae9c5c6fcbc9 Mon Sep 17 00:00:00 2001 From: "Joshua D. Drake" Date: Fri, 16 Oct 2026 16:51:11 +0000 Subject: [PATCH] test: differential's float sum/avg arm asks for two numbers, and its text-bloom C arm probes a value that is there (#1339) Two #1339 entries for test_differential.py, both [vacuous]. :291. "c_f4|c_f8 sum/avg returns the same shape" compared len() of a two-column row, which is 2 whatever the data. With the columnar float column set to NULL (a fixture mutant), main's arm PASSED, then the test died on an unnamed TypeError in the tolerance below. It is now " sum/avg returns two numbers on both sides", and under that mutant it is red with the values: "columnar (None, None) heap (33773584.0, ...)". :842. "textbloom C eq" probed tc = 'c123'. tc holds 16,000 distinct values of 50,000 and 'c123' is not one, so both sides counted 0, and a bloom wrongly skipping the chunk could not show. Both twins now probe row 7's value, 'c' || ((7*40503)%50000), the way "textbloom present" probes tk. Measured, PG17: - the reader's bloom check skipping every chunk: main's shell passes "textbloom C eq"; this shell reddens it; - the same, only under the C collation: main's pytest passes 7/7; this pytest reddens it, "got ['(0,)'] want ['(1,)']", and this shell reddens it as its only red (203 + 1 = 204). That log dates the row; census 2990 -> 2989 never, 1199 -> 1200 dated. Co-Authored-By: Claude Opus 5.5 --- test/check_ledger.tsv | 2 +- test/check_ledger_budget.txt | 12 +++++++++++- test/differential.sh | 5 ++++- test/pytest/test_differential.py | 14 ++++++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/test/check_ledger.tsv b/test/check_ledger.tsv index 9ba482f2b..ab3782fc5 100644 --- a/test/check_ledger.tsv +++ b/test/check_ledger.tsv @@ -768,7 +768,7 @@ differential differential stripe boundary N=2001 agg 15;16;17;18;19 never - differential differential stripe boundary N=2001 scan 15;16;17;18;19 never - differential differential stripe boundary N=2001 stripes 15;16;17;18;19 never - differential differential textbloom C absent 15;16;17;18;19 never - -differential differential textbloom C eq 15;16;17;18;19 never - +differential differential textbloom C eq 15;16;17;18;19 2026-10-16 the reader's bloom check skipping every chunk under the C collation (mutation, #1339) differential differential textbloom absent 15;16;17;18;19 never - differential differential textbloom collate-mismatch 15;16;17;18;19 never - differential differential textbloom present 15;16;17;18;19 never - diff --git a/test/check_ledger_budget.txt b/test/check_ledger_budget.txt index e9dc0d4b3..a14fe9df7 100644 --- a/test/check_ledger_budget.txt +++ b/test/check_ledger_budget.txt @@ -3619,4 +3619,14 @@ suites_not_covered 185 # never 3074 + 5 = 3079 # dated 1366 + 3 = 1369 # 3079 + 1369 == 4448 -checks_never_observed_red 3079 +# #1339, differential's text-bloom C arm probes a value that is there (jdatcmd): +# 3079 -> 3078. "textbloom C eq" probed tc = 'c123', which is in no row, so both +# sides counted 0; it now probes row 7's value, and the reader's bloom check +# skipping every chunk under the C collation dates it. No row is added, removed or +# renamed. +# +# ledger rows 4448 + 0 = 4448 +# never 3079 - 1 = 3078 +# dated 1369 + 1 = 1370 +# 3078 + 1370 == 4448 +checks_never_observed_red 3078 diff --git a/test/differential.sh b/test/differential.sh index 2c3e613d7..c55e50828 100755 --- a/test/differential.sh +++ b/test/differential.sh @@ -344,7 +344,10 @@ load_pair "SELECT g, 'k' || ((g*2654435761)%50000), 'c' || ((g*40503)%50000) diff_query "textbloom present" "SELECT id FROM %T WHERE tk = 'k' || ((7*2654435761)%50000)" diff_query "textbloom absent" "SELECT count(*) FROM %T WHERE tk = 'zzzzzzzz'" diff_query "textbloom C absent" "SELECT count(*) FROM %T WHERE tc = 'zzzzzzzz'" -diff_query "textbloom C eq" "SELECT count(*) FROM %T WHERE tc = 'c123'" +# A PRESENT value (#1339). 'c123' is in no row (tc is 16,000 distinct values of 50,000), +# so both sides counted 0 and a bloom wrongly skipping the chunk could not show. Row 7's +# value is present exactly once, the way "textbloom present" probes tk. +diff_query "textbloom C eq" "SELECT count(*) FROM %T WHERE tc = 'c' || ((7*40503)%50000)" # mismatched explicit COLLATE must still return correct results (not pushed) diff_query "textbloom collate-mismatch" "SELECT count(*) FROM %T WHERE tk = 'k100' COLLATE \"C\"" diff --git a/test/pytest/test_differential.py b/test/pytest/test_differential.py index 4c190dc76..e56088e64 100644 --- a/test/pytest/test_differential.py +++ b/test/pytest/test_differential.py @@ -288,7 +288,15 @@ def test_sum_and_avg_agree_for_every_numeric_type(matrix, col, expect): expect.row_set(c, h, f"{col} sum/avg") return cs, hs = c[0], h[0] - expect.num(len(cs), len(hs), f"{col} sum/avg returns the same shape") + # TWO NUMBERS ON BOTH SIDES, not the same length (#1339). len() of a two-column row + # is 2 whatever the data, so a columnar sum of NULL passed that arm and then raised + # an unnamed TypeError in the tolerance below. + numbers = all(isinstance(v, (int, float)) for v in (*cs, *hs)) + expect.text("two numbers each" if numbers and len(cs) == len(hs) == 2 + else f"columnar {cs!r} heap {hs!r}", + "two numbers each", f"{col} sum/avg returns two numbers on both sides") + if not numbers: + return for got, want, what in zip(cs, hs, ("sum", "avg")): # THE MEASUREMENT BELONGS IN THE COMPARED VALUE, NOT IN THE NAME. This put # `got`, `want` and `rel` into the check NAME, which does carry them to the @@ -839,7 +847,9 @@ def test_text_bloom_equality_agrees_including_a_mismatched_collation(pgc_conn, e ("textbloom present", "SELECT id FROM %T WHERE tk = 'k' || ((7*2654435761)%50000)"), ("textbloom absent", "SELECT count(*) FROM %T WHERE tk = 'zzzzzzzz'"), ("textbloom C absent", "SELECT count(*) FROM %T WHERE tc = 'zzzzzzzz'"), - ("textbloom C eq", "SELECT count(*) FROM %T WHERE tc = 'c123'"), + # A PRESENT value (#1339): 'c123' is in no row, so both sides counted 0 + # and a wrongly skipped chunk could not show. Row 7's value is present once. + ("textbloom C eq", "SELECT count(*) FROM %T WHERE tc = 'c' || ((7*40503)%50000)"), ("textbloom collate-mismatch", f"SELECT count(*) FROM %T WHERE tk = '{present}' COLLATE \"C\"")): c, h = p.both(sql)