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)