Repository navigation
test: seven #1339 projection findings, each red-first in both twins (#1339) - #1674
Conversation
… stale stored digest no longer passes (#1339) #1339 listed "premise: a tdigest stored in a columnar table still answers" as mislabelled: nothing compared the stored digests, so a pgColumnar storage change could redden that premise alone, or pass it. hll's stored sketch has had a byte-for-byte arm; tdigest had none. New arm, in both harnesses: "a tdigest stored in a columnar table comes back byte for byte". The daily digests are built once into a heap table and stored from there, because a tdigest depends on the order its inputs arrive in; digests rebuilt from heap could differ for reasons that are not storage. The union-within-0.1% check keeps its premise label: a storage change that altered a digest now reddens the arm first, the suite's rule for its bounds. Measured on PG17: - Mutant: a reader returning each long value (over 512 bytes) as the previous long value read (columnar_reader.c). Main's suite: 11 + 1 failed, only "an hll stored in a columnar table comes back byte for byte"; every tdigest check passed. This branch: 11 + 2 failed, the new arm "got [f] want [t]" and the hll arm; the twin fails the same two names (read from its own records). - Rejected mutants: flipping the last byte of a long value, or zeroing its second half, makes tdigest's output function take the server down, so every later arm fails as collateral. Not used for dating. - Clean: shell 13/13 on PG15 to PG18, 7 + 6 skipped on PG19 (no hll there); the twin passes on each; the new arm passes on all five. - compare_to_bash: 15 literal matches, 0 missing. Guards 474 passed. Ledger: 6141 + 1 = 6142 rows, 3863 never (unchanged), 2278 -> 2279 dated. suites_not_covered and cluster_tests unchanged. Stacked on #1674; the ledger gate passes, and guards 476 passed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
OffgridwithJD
left a comment
There was a problem hiding this comment.
@jdatcmd Approving 88393cd8. Measured on ~/pg/17-w: shell, then the twin as shipped, main = f0b43802. Mutants in ~/pgc-tools/rv1674/.
| mutant | main shell / twin | this shell / twin |
|---|---|---|
dropunlocked (the unlocked owner check deleted, columnar_tableam.c:3236) |
8/8 / passed | 1 red / SAME 1 |
dropboth (both owner checks deleted) |
1 red / 1 | 3 red; the twin's first red is "and the two refusals are the same error, so neither leaks" |
nopagefloor (if (pages < 1) pages = 1 removed) |
11/11 / passed | 1 red: "and its scan is not priced at zero pages" / SAME |
noprojexec (if (false && cstate->projName != NULL) at Begin) |
76/76 / 34 passed | 1 red: "executing it reads the projection…" / SAME |
projpar1 (the projection path's parallel_workers = 1) |
3 red, two of them premise: |
the same 3 without the prefix / the twin's first |
noreadacl (mine: read_projection's SELECT check removed) |
4 red / 3 | 4 red / 3 |
bigfloor (mine, over-apply: every projection priced at ≥1000 pages) |
3 red / 2 | 3 red / 2 |
| clean, PG17 and PG18 | — | 11, 12, 13, 28, 78 / 1, 2, 1, 12, 34 passed, on both |
Where the counts differ (dropboth, bigfloor, projpar1, noreadacl), every twin red is in the shell's red set. The twin's multi-check tests stop at their first red, as your body says.
The ledger diff matches: the two projection_parallel renames, projection_drop_column seeded with 11 rows, and each newly dated row is one of the measured reds. "workers share the covering projection scan" moves from 2026-10-13 to 2026-10-22, which is right for a last-red column.
🤖 Generated with Claude Code
…1339) Fixes #1339 test_projection_drop_column.py:113 and :119, test_projection_parallel.py:217, test_projection_privilege.py:259, test_projection_scan_io.py:260 and test_projections.py:459 and :600, each in its shell twin too. For each finding, a one-line mutation of pgcolumnar was first run on main, where both twins stayed green (or reddened the wrong name). After the fix, the same mutation reddens the target by the same name in both twins. The pytest twin stops at a test's first red, so it can show fewer names than the shell suite. - #1339 test_projection_drop_column.py:113 (weak). The stranger arms read only 42501, so deleting the unlocked ownership check in columnar_tableam.c passed (main: 8/8 shell, twin green). New arm in both: while the owner holds SHARE, a stranger is refused at once. Under the mutation it waits out lock_timeout: "a non-owner is refused before it waits for the table lock" got 55P03. - #1339 test_projection_drop_column.py:119 (mislabelled, twin mismatch). "premise: and the two refusals are the same error" came after the two constant arms, so it could never run red; the shell suite did not have it. With both ownership checks deleted, main's twin reddened "a non-owner learns nothing". The check loses the prefix, moves first, and joins the shell suite. Under that mutation both twins now redden "and the two refusals are the same error, so neither leaks" (2BP01 against 42501). - #1339 test_projection_parallel.py:217 (mislabelled). With parallel_workers = 1 on the projection path, main reddened "premise: EXPLAIN ANALYZE launched two workers" in both twins (and the rows= premise in the shell suite). Both lose the prefix; the same mutation reddens them under their new names. - #1339 test_projection_privilege.py:259 (twin mismatch). The twin asserted 42501 (and 0A000 under the policy) twice on one error. With read_projection's aclcheck_error replaced by elog, main's shell reddened "and THAT refusal is the C check" and the twin reddened "a role with EXECUTE but no SELECT is refused read_projection". The twin's first arm now asks only "refused", as the shell does. The shell's reconstruct arms now read the SQLSTATE, as the twin did: main's shell stayed green with reconstruct's aclcheck_error replaced. Under each of the three mutations (both elogs, and the RLS refusal raised as 42501), the two twins now redden identical names. - #1339 test_projection_scan_io.py:260 (weak). With the one-page floor deleted, an empty projection is priced at 0 pages and is still chosen (main green in both). New arm in both: "and its scan is not priced at zero pages" (PG17: total cost 5.00, and 0.00 under the mutation). - #1339 test_projections.py:459 (weak). With the executor never opening the projection while EXPLAIN still names it, main was green in both. New arm in both reads EXPLAIN ANALYZE: the filter sees fewer rows than the base holds (5980 against 17980 on PG17; 17980 of 17980 under the mutation). - #1339 test_projections.py:600 (inaccurate). EXPLAIN shows that the count plans pmp (Columnar Projection: pmp), so the triage's guess that it reads the base did not hold. But nothing asserted it. With projection pages multiplied by 1000000, so the count plans the base, every :600 arm stayed green on main. New arm in both: "the old-snapshot count is planned as a projection scan". The triage's candidate mutation (GetLatestSnapshot() for the projection read only) stays green: the base liveness cache still uses the query snapshot. With both on GetLatestSnapshot(), main's shell reddened "old snapshot projection scan does not see post-snapshot rows" but the twin reddened "session A responded post-commit". The twin's two timeout arms now read whether session A answered. The shell's poll no longer accepts an ERROR that echoes the token as an answer: with session A unable to see pm, main's shell reddened the counts but never "session A opened snapshot". It does now, in both twins. Ledger: projection_drop_column is seeded (11 rows; suites_not_covered 104 -> 103). 19 rows are dated by ten mutations, 2 are renamed, and 14 are new. checks_never_observed_red 3868 -> 3863 (6141 rows = 3863 never + 2278 dated). Clean on 15-19 for every touched suite and twin. Gates: the ledger gate passes for all five suites, compare_to_bash finds every bash property covered for all five pairs, and the guard set passes (476 passed). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
88393cd to
0772367
Compare
OffgridwithJD
left a comment
There was a problem hiding this comment.
@jdatcmd Re-approved at 07723678. The restate changes only the bookkeeping: excluding the ledger, the budget, expected_tests.txt and TESTS.md, the commit's diff is byte-identical to 88393cd8's (same hash over the changed lines, and 12 files, +273/−43 both times). The tsv is sorted, and its census matches the budget's restated numbers.
🤖 Generated with Claude Code
|
Restated onto main 9ab6a4c (after #1740) as 0772367, because the budget conflicted.
@OffgridwithJD this needs a re-approval on 0772367 to merge. |
… stale stored digest no longer passes (#1339) as mislabelled: nothing compared the stored digests, so a pgColumnar storage change could redden that premise alone, or pass it. hll's stored sketch has had a byte-for-byte arm; tdigest had none. New arm, in both harnesses: "a tdigest stored in a columnar table comes back byte for byte". The daily digests are built once into a heap table and stored from there, because a tdigest depends on the order its inputs arrive in; digests rebuilt from heap could differ for reasons that are not storage. The union-within-0.1% check keeps its premise label: a storage change that altered a digest now reddens the arm first, the suite's rule for its bounds. Measured on PG17: - Mutant: a reader returning each long value (over 512 bytes) as the previous long value read (columnar_reader.c). Main's suite: 11 + 1 failed, only "an hll stored in a columnar table comes back byte for byte"; every tdigest check passed. This branch: 11 + 2 failed, the new arm "got [f] want [t]" and the hll arm; the twin fails the same two names (read from its own records). - Rejected mutants: flipping the last byte of a long value, or zeroing its second half, makes tdigest's output function take the server down, so every later arm fails as collateral. Not used for dating. - Clean: shell 13/13 on PG15 to PG18, 7 + 6 skipped on PG19 (no hll there); the twin passes on each; the new arm passes on all five. - compare_to_bash: 15 literal matches, 0 missing. Guards 474 passed. Ledger: 6141 + 1 = 6142 rows, 3863 never (unchanged), 2278 -> 2279 dated. suites_not_covered and cluster_tests unchanged. Stacked on #1674; the ledger gate passes, and guards 476 passed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… stale stored digest no longer passes (#1339) #1339 listed "premise: a tdigest stored in a columnar table still answers" as mislabelled: nothing compared the stored digests, so a pgColumnar storage change could redden that premise alone, or pass it. hll's stored sketch has had a byte-for-byte arm; tdigest had none. New arm, in both harnesses: "a tdigest stored in a columnar table comes back byte for byte". The daily digests are built once into a heap table and stored from there, because a tdigest depends on the order its inputs arrive in; digests rebuilt from heap could differ for reasons that are not storage. The union-within-0.1% check keeps its premise label: a storage change that altered a digest now reddens the arm first, the suite's rule for its bounds. Measured on PG17: - Mutant: a reader returning each long value (over 512 bytes) as the previous long value read (columnar_reader.c). Main's suite: 11 + 1 failed, only "an hll stored in a columnar table comes back byte for byte"; every tdigest check passed. This branch: 11 + 2 failed, the new arm "got [f] want [t]" and the hll arm; the twin fails the same two names (read from its own records). - Rejected mutants: flipping the last byte of a long value, or zeroing its second half, makes tdigest's output function take the server down, so every later arm fails as collateral. Not used for dating. - Clean: shell 13/13 on PG15 to PG18, 7 + 6 skipped on PG19 (no hll there); the twin passes on each; the new arm passes on all five. - compare_to_bash: 15 literal matches, 0 missing. Guards 474 passed. Ledger: 6141 + 1 = 6142 rows, 3863 never (unchanged), 2278 -> 2279 dated. suites_not_covered and cluster_tests unchanged. Stacked on #1674; the ledger gate passes, and guards 476 passed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@OffgridwithJD: this fixes seven #1339 findings in the projection suites. Each fix covers both twins.
What changed, per finding
For each finding I first ran a one-line pgcolumnar mutation on main. Then I fixed the check and ran the same mutation again. "Green" means the suite passed. The pytest twin stops at a test's first red, so where the shell suite reds more names, the twin shows the first one.
columnar_tableam.c: the unlocked DROP COLUMN ownership check deletedcolumnar_customscan.c: projection pathparallel_workers = 1aclcheck_errorreplaced byelog(XX000)aclcheck_errorreplaced byelogreconstruct:names / FAIL "and is refused reconstruct_via_projection, which leaks..." + those 2PgColumnarRequireNoRowSecurityraises 42501, not 0A000if (pages < 1) pages = 1;deletedif (false && cstate->projName != NULL)), but EXPLAIN still names itGetLatestSnapshot()Notes on :600
pmp, with no ANALYZE. So the triage's guess that it reads the base did not hold. Nothing asserted the plan, though, so a new arm checks it in both twins.GetLatestSnapshot()for the projection read only) stays green on main and after the fix. The base liveness cache still uses the query snapshot and hides it. The two-site mutation above is the one that reddens the arm.search_pathset topg_catalog, the shell on the old poll reddened only the counts. Both twins now redden "session A opened snapshot".Clean runs
Every touched suite and twin is green on PG15, 16, 17, 18 and 19:
One PG18 twin run (projection_parallel) first errored at setup with "Address already in use", because another agent's cluster held the port. It passed when re-run. After the rebase onto f0b4380, all five suites and twins are green again on PG17.
Gates
LC_ALL=C sort -cpasses.Ledger
Not in scope
The
USAGE_ONLYpair in projection_privilege has the same mismatch: the twin asserts 42501 and the shell suite asserts "refused". It was not in this batch, so it is unchanged.Re-checked by me before pushing (PG17, main f0b4380):
:260, the one-page floor removed (customscan.c:2789): main's shell 11/11; this shell 1 failed, "and its scan is not priced at zero pages: got [free at 0.00] want [priced]"; the twin fails the same test.🤖 Generated with Claude Code