Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,69 @@ true until the next version shipped.

Guard half 347 passed, 913 checks; the two cluster-side files touched, 22 passed,
79 checks. `guard_tests` re-derived by collection, 346 -> 347.
- A mutation with two genuine targets could not be recorded at all (#1014).

`merge --mutation NAME` refused any run in which more than one check failed. The
guard is right about the hazard -- attributing a mutation to a bystander records
collateral damage as evidence -- and wrong about the remedy. A mutation with TWO
GENUINE targets is ordinary, and for it the only permitted merge was
`--reds-are-real`, which writes `-` in the mutation column.

So the catalogue that column exists to become could never hold the entry it most
exists for: the one that says WHICH CHECKS SHARE A CAUSE.

`--target CHECK`, repeatable, makes the caller assert the attribution, exactly as
`--reds-are-real` makes them assert that a red is real. The guard keeps its teeth
in both directions:

a red not named as a target refused, and the refusal names it
a target that did not fail refused, because the claim is wrong
--target without --mutation refused, because it attributes nothing
one red and no --target merges, as every existing caller does

THE TWO ROWS ARE BACK-FILLED, and the mutation was re-run rather than recalled.
`native_join_runtime_filter`'s two ever-red rows now name
`pgcolumnar.enable_join_runtime_filter boot value true -> false` instead of `-`.

Getting there cost two wrong attempts, and the reason is a PostgreSQL fact worth
writing down because it is not the obvious one.

Mutating only the C initializer is INERT: 46 checks, 0 failed.
`DefineCustomBoolVariable` assigns the boot value to the variable at registration,
so for a bool GUC the initializer decides nothing at run time.

Mutating only the `DefineCustomBoolVariable` boot value will not start at all:

LOG: GUC (PGC_BOOL) pgcolumnar.enable_join_runtime_filter, boot_val=0, C-var=1
TRAP: failed Assert("check_GUC_init(variable)"), File: "guc.c", Line: 4944

`check_GUC_init` does NOT require the two to agree. Read at the source rather
than inferred from the trap, `src/backend/utils/misc/guc.c`:

case PGC_BOOL:
if (*conf->variable && !conf->boot_val) /* traps: C-var true, boot false */

It is ASYMMETRIC: a C variable left `true` against a `false` boot value traps, and
a `false` initializer against a `true` boot value is accepted silently. `PGC_INT`
and `PGC_REAL` apply the same asymmetry against zero; `PGC_ENUM` requires equality
unconditionally.

So this mutation is two lines, but not because the two must match -- because one
line alone does nothing and the other alone will not boot. The third attempt
changed both, fingerprinted the `.so` before and after to prove the build took
(`53c3f626889c` -> `7f67e077f26c`), reddened exactly the two checks, and restored
byte-exact with the `.so` back to `53c3f626889c`.

The census does not move: both rows were already ever-red, so
`checks_never_observed_red` stays at 1277 and the ledger stays 1285 rows, all
carrying `15;16;17;18;19`.

Both harnesses, independently: nine arms in `selftest/410` and six in
`test_mutation_ledger.py`, each with its own fixture and its own names. Every
refusal arm greps its MESSAGE rather than only its status -- `--target` did not
exist before this change, so argparse exited 2 for an unknown flag, and three arms
written against the status alone passed against the absent feature. Measured
before the implementation, which is why they are written the other way.

- Four secret-leak claims over the PG server log could pass having read nothing
(#1032).
Expand Down
14 changes: 12 additions & 2 deletions test/check_ledger.tsv
Original file line number Diff line number Diff line change
Expand Up @@ -880,6 +880,7 @@ harness_selftest 410-a-check-must-have-been-red --mutation across two failing ch
harness_selftest 410-a-check-must-have-been-red --prune REFUSES the whole prune when any orphan carries history 15;16;17;18;19 never -
harness_selftest 410-a-check-must-have-been-red --prune removes NOTHING from a part that skipped 15;16;17;18;19 never -
harness_selftest 410-a-check-must-have-been-red --prune removes a historyless orphan and says which 15;16;17;18;19 never -
harness_selftest 410-a-check-must-have-been-red --target without --mutation is refused, because it attributes nothing 15;16;17;18;19 never -
harness_selftest 410-a-check-must-have-been-red a PG15 run names no orphan, having seen every check the ledger claims for 15 15;16;17;18;19 never -
harness_selftest 410-a-check-must-have-been-red a before-log and an after-log together are refused, as rename-scan refuses them 15;16;17;18;19 never -
harness_selftest 410-a-check-must-have-been-red a before-log and an after-log together are refused, not silently empty 15;16;17;18;19 never -
Expand Down Expand Up @@ -921,6 +922,7 @@ harness_selftest 410-a-check-must-have-been-red a second major is ADDED to the s
harness_selftest 410-a-check-must-have-been-red a second mutation ACCUMULATES rather than replacing the first 15;16;17;18;19 never -
harness_selftest 410-a-check-must-have-been-red a skipped part is NOT an orphan, so --orphans-only returns 0 15;16;17;18;19 never -
harness_selftest 410-a-check-must-have-been-red a stale prior is named WITH its distance from HEAD 15;16;17;18;19 never -
harness_selftest 410-a-check-must-have-been-red a target that did not fail is refused, because the claim is wrong 15;16;17;18;19 never -
harness_selftest 410-a-check-must-have-been-red a verdict pgc_record cannot emit is an integrity failure 15;16;17;18;19 never -
harness_selftest 410-a-check-must-have-been-red an absolute budget path resolves against git rather than shrugging 15;16;17;18;19 never -
harness_selftest 410-a-check-must-have-been-red an empty check name is one too, because it names no check 15;16;17;18;19 never -
Expand Down Expand Up @@ -963,6 +965,8 @@ harness_selftest 410-a-check-must-have-been-red and one that stayed green keeps
harness_selftest 410-a-check-must-have-been-red and only when there is a base, so a push build does not fail on it 15;16;17;18;19 never -
harness_selftest 410-a-check-must-have-been-red and records neither as ever having been red 15;16;17;18;19 never -
harness_selftest 410-a-check-must-have-been-red and that is not a failure, because a run sees ONE major 15;16;17;18;19 never -
harness_selftest 410-a-check-must-have-been-red and that refusal names the target that stayed green 15;16;17;18;19 never -
harness_selftest 410-a-check-must-have-been-red and that refusal says --target needs --mutation 15;16;17;18;19 never -
harness_selftest 410-a-check-must-have-been-red and the PG18-only row is one it cannot speak about, not one that vanished 15;16;17;18;19 never -
harness_selftest 410-a-check-must-have-been-red and the addition in the other part is not called a rename 15;16;17;18;19 never -
harness_selftest 410-a-check-must-have-been-red and the census line it always printed is still there 15;16;17;18;19 never -
Expand All @@ -973,10 +977,12 @@ harness_selftest 410-a-check-must-have-been-red and the history it is about to l
harness_selftest 410-a-check-must-have-been-red and the history it would lose is printed with it 15;16;17;18;19 never -
harness_selftest 410-a-check-must-have-been-red and the ledger is one row shorter afterwards 15;16;17;18;19 never -
harness_selftest 410-a-check-must-have-been-red and the message says how to fix it, because regenerating is the intended action 15;16;17;18;19 never -
harness_selftest 410-a-check-must-have-been-red and the mutation is recorded against both of them 15;16;17;18;19 never -
harness_selftest 410-a-check-must-have-been-red and the prior policy is chosen from a closed set, not taken as a ref 15;16;17;18;19 never -
harness_selftest 410-a-check-must-have-been-red and the refusal names both values 15;16;17;18;19 never -
harness_selftest 410-a-check-must-have-been-red and the refusal names how many failed, so the author can narrow the run 15;16;17;18;19 never -
harness_selftest 410-a-check-must-have-been-red and the refusal names the check that reddened 15;16;17;18;19 never -
harness_selftest 410-a-check-must-have-been-red and the refusal names the check that was not claimed as a target 15;16;17;18;19 never -
harness_selftest 410-a-check-must-have-been-red and the refusal names the raise 15;16;17;18;19 never -
harness_selftest 410-a-check-must-have-been-red and the refusal quotes the claim beside the measurement 15;16;17;18;19 never -
harness_selftest 410-a-check-must-have-been-red and the runner names no remote at that call site 15;16;17;18;19 never -
Expand All @@ -997,10 +1003,12 @@ harness_selftest 410-a-check-must-have-been-red control: a well-formed log still
harness_selftest 410-a-check-must-have-been-red control: all-clean logs leave both flags down 15;16;17;18;19 never -
harness_selftest 410-a-check-must-have-been-red control: an all-PASS log still merges with no flag at all 15;16;17;18;19 never -
harness_selftest 410-a-check-must-have-been-red control: and a clean run still returns 0 under it 15;16;17;18;19 never -
harness_selftest 410-a-check-must-have-been-red control: and still records the mutation against the check that reddened 15;16;17;18;19 never -
harness_selftest 410-a-check-must-have-been-red control: and that one did prune, so 0 is not a refusal in disguise 15;16;17;18;19 never -
harness_selftest 410-a-check-must-have-been-red control: and the ledger really is shorter afterwards 15;16;17;18;19 never -
harness_selftest 410-a-check-must-have-been-red control: on a covered major a new check is named and refused 15;16;17;18;19 never -
harness_selftest 410-a-check-must-have-been-red control: one orphan among clean logs raises only the orphan flag 15;16;17;18;19 never -
harness_selftest 410-a-check-must-have-been-red control: one red and no --target still merges 15;16;17;18;19 never -
harness_selftest 410-a-check-must-have-been-red control: the row in the part the run never mentioned SURVIVES the prune 15;16;17;18;19 never -
harness_selftest 410-a-check-must-have-been-red control: the same log merges with a different reason, so the refusal above is --mutation-across-two-checks and not the log 15;16;17;18;19 never -
harness_selftest 410-a-check-must-have-been-red control: the same row with a real major is read without complaint 15;16;17;18;19 never -
Expand All @@ -1011,6 +1019,8 @@ harness_selftest 410-a-check-must-have-been-red every row has six fields and no
harness_selftest 410-a-check-must-have-been-red it is still a finding, so the scan does not return success 15;16;17;18;19 never -
harness_selftest 410-a-check-must-have-been-red lowering it is allowed, which is the direction the burn-down goes 15;16;17;18;19 never -
harness_selftest 410-a-check-must-have-been-red merging a green run records both checks 15;16;17;18;19 never -
harness_selftest 410-a-check-must-have-been-red naming both failing checks as targets permits the merge 15;16;17;18;19 never -
harness_selftest 410-a-check-must-have-been-red naming only one of two failing checks is still refused 15;16;17;18;19 never -
harness_selftest 410-a-check-must-have-been-red nor is one merely removed 15;16;17;18;19 never -
harness_selftest 410-a-check-must-have-been-red once the suite is covered, a new check in it IS refused 15;16;17;18;19 never -
harness_selftest 410-a-check-must-have-been-red one --mutation cannot be attributed across several runs at once 15;16;17;18;19 never -
Expand Down Expand Up @@ -1266,7 +1276,7 @@ native_join_runtime_filter native_join_runtime_filter collation-mismatch answer
native_join_runtime_filter native_join_runtime_filter collation-mismatch bloom rejects most non-matches 15;16;17;18;19 never -
native_join_runtime_filter native_join_runtime_filter collation-mismatch interval stays off 15;16;17;18;19 never -
native_join_runtime_filter native_join_runtime_filter collation-mismatch plan has coordinator 15;16;17;18;19 never -
native_join_runtime_filter native_join_runtime_filter default plan has runtime coordinator 15;16;17;18;19 2026-09-12 -
native_join_runtime_filter native_join_runtime_filter default plan has runtime coordinator 15;16;17;18;19 2026-09-18 pgcolumnar.enable_join_runtime_filter boot value true -> false
native_join_runtime_filter native_join_runtime_filter early limit equals heap 15;16;17;18;19 never -
native_join_runtime_filter native_join_runtime_filter early limit has coordinator 15;16;17;18;19 never -
native_join_runtime_filter native_join_runtime_filter empty-build answer equals heap 15;16;17;18;19 never -
Expand All @@ -1278,7 +1288,7 @@ native_join_runtime_filter native_join_runtime_filter int4/int8 answer equals he
native_join_runtime_filter native_join_runtime_filter int4/int8 bloom rejects most non-matches 15;16;17;18;19 never -
native_join_runtime_filter native_join_runtime_filter int4/int8 interval stays off 15;16;17;18;19 never -
native_join_runtime_filter native_join_runtime_filter int4/int8 plan has coordinator 15;16;17;18;19 never -
native_join_runtime_filter native_join_runtime_filter join runtime filter defaults on 15;16;17;18;19 2026-09-12 -
native_join_runtime_filter native_join_runtime_filter join runtime filter defaults on 15;16;17;18;19 2026-09-18 pgcolumnar.enable_join_runtime_filter boot value true -> false
native_join_runtime_filter native_join_runtime_filter plan has build tap 15;16;17;18;19 never -
native_join_runtime_filter native_join_runtime_filter plan has runtime coordinator 15;16;17;18;19 never -
native_join_runtime_filter native_join_runtime_filter plan retains core Hash Join 15;16;17;18;19 never -
Expand Down
2 changes: 1 addition & 1 deletion test/check_ledger_budget.txt
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,4 @@ suites_not_covered 249
# clean union and only the COUNT had to be re-derived. Both files conflicted loudly
# here, which is the safe half: the ledger is always right and only sometimes speaks.
# Re-derived by the command above on the merged tree, never by adding the deltas:
checks_never_observed_red 1330
checks_never_observed_red 1340
58 changes: 55 additions & 3 deletions test/pgc_ledger.py
Original file line number Diff line number Diff line change
Expand Up @@ -459,17 +459,65 @@ def cmd_merge(args):
# kills that check, which is the opposite of what this column is for.
# Measured on #918: a two-FAIL log merged with --mutation MUTATION_A recorded
# it against both. Reported by @linuxhikerpm.
# --target NAMES WHAT THE MUTATION KILLED, and means nothing on its own (#1014).
if getattr(args, "target", None) and not args.mutation:
raise LedgerError(
"--target names what a mutation killed, so it needs --mutation to say "
"which mutation. On its own it attributes nothing.")

if args.mutation:
failed = sorted({key for _p, seen in runs for key, vs in seen.items()
if any(v == "FAIL" for v, _m in vs)})
if len(failed) > 1:
named = list(dict.fromkeys(getattr(args, "target", None) or []))
if named:
# A MUTATION WITH TWO GENUINE TARGETS IS ORDINARY, not exotic (#1014).
# Reverting the `enable_join_runtime_filter` boot value reddens both
# `join runtime filter defaults on` and `default plan has runtime
# coordinator`; neither is collateral, because both read the default
# directly, which is why one change kills both.
#
# The tool cannot tell that from "one target and one bystander", and it
# used to resolve the ambiguity by recording NOTHING: the only permitted
# merge was --reds-are-real, which writes `-` in the mutation column. So
# the catalogue this column exists to become could never hold the entry
# it most exists for -- the one that says WHICH CHECKS SHARE A CAUSE.
#
# The caller ASSERTS the attribution instead, exactly as --reds-are-real
# makes them assert that a red is real. The guard keeps its teeth in the
# case it was written for: a red not named is still collateral.
#
# Matched on the check NAME, which is what an author knows and what the
# PR body will say. The ledger key carries suite and part as well, so a
# name shared by two parts names both -- deliberately: they are the same
# assertion made twice, and a mutation that kills one kills the other.
failed_names = {n for _s, _p, n in failed}
unhit = [t for t in named if t not in failed_names]
if unhit:
listed = "\n".join(f" {t}" for t in unhit[:6])
more = "" if len(unhit) <= 6 else f"\n ... and {len(unhit) - 6} more"
raise LedgerError(
f"{len(unhit)} check(s) were named as a target but did not fail in "
f"this run:\n{listed}{more}\n A mutation that does not redden "
f"what you aimed at is a finding about the mutation, not a row to "
f"write down. Re-run, or drop the target you did not hit.")
unnamed = [k for k in failed if k[2] not in set(named)]
if unnamed:
listed = "\n".join(f" {s}\t{p}\t{n}" for s, p, n in unnamed[:6])
more = "" if len(unnamed) <= 6 else f"\n ... and {len(unnamed) - 6} more"
raise LedgerError(
f"{len(unnamed)} check(s) failed but was not named as a target:\n"
f"{listed}{more}\n An unnamed red is collateral damage, and "
f"recording the mutation against it would be evidence that the "
f"mutation kills that check. Name it with --target if it is a real "
f"target, or narrow the run.")
elif len(failed) > 1:
listed = "\n".join(f" {s}\t{p}\t{n}" for s, p, n in failed[:6])
more = "" if len(failed) <= 6 else f"\n ... and {len(failed) - 6} more"
raise LedgerError(
f"--mutation names one check, but {len(failed)} checks failed in this "
f"run:\n{listed}{more}\n Attributing it to all of them would record "
f"collateral damage as evidence. Merge without --mutation, or narrow the "
f"run to the check the mutation targets.")
f"collateral damage as evidence. Name every one you meant to hit with "
f"--target, merge without --mutation, or narrow the run.")

# A RED NEEDS A REASON (#946). `merge` already refuses a log that does not
# RECONCILE, and reconciliation is not the property that matters: both logs that
Expand Down Expand Up @@ -1252,6 +1300,10 @@ def main(argv=None):
m.add_argument("--ledger", required=True)
m.add_argument("--date", default="unknown")
m.add_argument("--mutation", default="")
m.add_argument("--target", action="append", default=[], metavar="CHECK",
help="a check name this mutation was aimed at; repeatable. "
"Required when a mutation reddens more than one check, so "
"the attribution is asserted rather than inferred (#1014)")
m.add_argument("--reds-are-real", action="store_true",
help="the FAIL records in these logs are a genuine observation "
"of the code under test, not an artifact of the environment")
Expand Down
Loading
Loading