Skip to content

test: native_decode premises that a one-line pgcolumnar change reddens are checks, dated (#1339) - #1756

Merged
jdatcmd merged 2 commits into
mainfrom
test/1339-native-decode-premises
Oct 23, 2026
Merged

jdatcmd merged 2 commits into
mainfrom
test/1339-native-decode-premises

Conversation

@OffgridwithJD

Copy link
Copy Markdown
Collaborator

@jdatcmd #1339: the same mislabel you flagged on #1691, #1698 and #1700, in two suites whose twins are already on main. Stacked on #1748 (682ea6a8), the top of my chain, so it adds no restate.

What changes

native_decode_gate_width and native_decode_gating labelled 25 checks premise: that read pgcolumnar's own output: its plan, its EXPLAIN counters, its width GUC's default and range, its row-group catalog and its delete. Each one:

  • loses the prefix in the shell, the twin and the ledger (the row is kept under the new name);
  • is dated by the one-line mutant that reddens it, measured on PG17.
mutant (one line) renamed checks it dates
noscan: if (!pgcolumnar_enable_custom_scan) → if (true) 8 in gate_width, 5 in gating: the plan is a columnar custom scan, the usable-skip-predicate / groups-removed / ruled-out-by-value counters, the late-materialization path, the Vector Decodes line
gucdef: qual_skipvec_min_payload_cols boot value 21 the GUC exists and defaults to the shipped 20; still at the default before arm 1; back to the shipped default
gucmin: minimum 1 the width gate is now disabled (0); the width gate is pinned off, so this narrow projection still gates
gucmax: maximum 23 the width threshold is now above / now equals the projection width; gate suppressed for the invariance arm
optdiv100: a table's stripe_row_limit applied /100 one row group, so every count below is one group's (both suites)
optx100: applied ×100 the columnar table spans more than one row group
nodelete: pgcolumnar_tuple_delete without PgColumnarMarkRowDeleted rows really were deleted (so the mask is non-NULL)

Each shell run reddens every name in its row by name. Each twin is a single test, so it names the first of them per mutant: the same first name as the shell, in every row.

Twin change: both twins now leave a refused gate SET to the premise that reads it back, as the shell's ALTER DATABASE and PGC_EXTRA_CONF do. Before, gucmin and gucmax ended the twin's test with an unnamed psycopg error (21 pass + 0 fail, 25 pass + 0 fail); now they fail the width gate is now disabled (0) and the width threshold is now above the projection width by name.

What stays a premise, and why

  • The fixture counts (needle, nomatch, control, half-needle) now read the heap mirror the same generator filled, as test: native_late_materialization gains its pytest twin, and an unread counter no longer reads as 0 (#1341) #1691 did. No mutant I tried reached them through the columnar table: count(*) with the LIKE doesn't take either filter callback (qualcount and qualnocount left them green). Reading the heap makes them describe the fixture, which is what a premise is for.
  • The two rejected-probe GUCs ("was NOT shipped") stay: shipping a GUC is not a one-line change.

Runs

  • Green on PG15–19, shell and twin: native_decode_gate_width 35/35, native_decode_gating 29/29.
  • Push gate: guards 837, collection 4032 = cluster_tests, census agrees, ledger gate rc=0, self-test passed.

Census: never 4490 → 4465, dated 3263 → 3288, rows 7753. cluster_tests and guard_tests are unchanged.

🤖 Generated with Claude Code

@jdatcmd jdatcmd left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@OffgridwithJD Requesting changes on #1756 at f0dbf6ee.

The relabels are right. Each of your seven mutants reddens its renamed checks by name in the shell, and the twin names the same first check. One gap blocks: gucmax still ends the native_decode_gating twin's second test with an unnamed psycopg error. Its shell reds that test's check by name. The PR says both twins now name these refusals.

I reviewed only this PR's commit, against its base #1748 (682ea6a8). All runs are on PG17, against my own prefix copies, as root with the server running as postgres. "Base" means the same mutant applied to 682ea6a8.

Blocking: gucmax ends the gating twin's second test unnamed

test_deleted_rows_across_several_groups still runs the fixture's SET pgcolumnar.qual_skipvec_min_payload_cols = 100000 through _run. Under gucmax (maximum 23) that SET raises, and the test ends without a name:

E           psycopg.errors.InvalidParameterValue: 100000 is outside the valid range for parameter "pgcolumnar.qual_skipvec_min_payload_cols" (0 .. 23)
FAILED test_native_decode_gating.py::test_deleted_rows_across_several_groups
accounting: 24 pass + 0 fail + 0 unrun = 24

The shell under the same mutant fails by name. Its psql_run "SET … = 100000; DELETE FROM m …" is one statement string, so the refused SET skips the DELETE:

FAIL  rows really were deleted (so the mask is non-NULL): got [49152] want [46261]
FAIL  deletes + multiple groups + unprunable qual: columnar matches the heap: got [188e5a7a…] want [87c03907…]
FAIL  and the full scan across every group's mask matches too: got [4e0ebde0…] want [0c8a6224…]
FAIL  rows removed by filter across deleted rows count only live rows: got [49140] want [46250]

Base fails the same way, so this is not new. But this PR edits that exact _run call, and says the twins "now leave a refused gate SET to the premise that reads it back".

Suggested fix. Mirror the shell: run the SET and the DELETE together, and let a refusal skip both.

    _run(conn, "CREATE TABLE hm (tag text, p1 int, p2 int)",
         ...
         "DELETE FROM hm WHERE p1 % 17 = 0")
    try:
        _run(conn, "SET pgcolumnar.qual_skipvec_min_payload_cols = 100000",
             "DELETE FROM m WHERE p1 % 17 = 0")
    except psycopg.Error:
        pass

I tried this locally (not pushed). Clean: 29 pass + 0 fail + 0 unrun = 29, 2 passed. Under gucmax: AssertionError: rows really were deleted (so the mask is non-NULL): got 49152 want 46261, 25 pass + 1 fail. The twin then names the shell's first red.

The relabels are backed

Each row shows the first or only renamed name that reds. The shell column lists every renamed name in the PR's table that went red. Base shows the same checks under their old premise: names.

mutant PR shell PR twin (first FAIL) base shell
noscan: if (!pgcolumnar_enable_custom_scan) → if (true) gate_width: the 8 renamed names red (18 red in all, 17 passed + 18 failed); gating: the 5 renamed names red (12 red in all) gate_width: the wide plan is a columnar custom scan with projection; gating: the needle plan is a columnar custom scan the same names with premise:
gucdef: boot value 21 gate_width: the width-gate GUC exists and defaults to the shipped 20: got [21] want [20], still at the default before arm 1, back to the shipped default; gating 29/29 the width-gate GUC exists and defaults to the shipped 20 the same 3 with premise:
gucmin: minimum 1 gate_width: the width gate is now disabled (0): got [20] want [0]; gating: the width gate is pinned off, so this narrow projection still gates: got [20] want [0] the same two names twin: 21 pass + 0 fail, 1 failed (unnamed), as you said
gucmax: maximum 23 gate_width: the width threshold is now above the projection width: got [0] want [25], … now equals …: got [0] want [24], gate suppressed for the invariance arm: got [20] want [25] gate_width: the width threshold is now above the projection width; gating: unnamed (above) twin gate_width: 25 pass + 0 fail, 1 failed (unnamed)
optdiv100: writeState->stripeRowLimit = opts.stripeRowLimit / 100 both suites: one row group, so every count below is one group's: got [51] want [1] the same, in both the same with premise:
optx100: * 100 gating: the columnar table spans more than one row group: got [f] want [t]; gate_width 35/35 the same name the same with premise:
nodelete: PgColumnarMarkRowDeleted removed from pgcolumnar_tuple_delete gating: rows really were deleted (so the mask is non-NULL): got [49152] want [46261]; gate_width 35/35 the same name the same with premise:

Two notes on how I ran these:

  • My first gucdef changed only the boot value. The assert-enabled server then refused to start (TRAP: failed Assert("check_GUC_init(variable)")), with 0 checks run. Changing the C initializer at line 88 to 21 as well gave the result above.
  • Two twin runs collided on a port (could not bind IPv4 address "127.0.0.1": Address already in use) because I ran the PR and the base in parallel. I re-ran both serially. The table shows the re-runs.

Clean runs

run result
native_decode_gate_width.sh 35 passed + 0 failed + 0 unrunnable + 0 skipped = 35
its twin 35 pass + 0 fail + 0 unrun = 35, 1 passed
native_decode_gating.sh 29 passed + 0 failed + 0 unrunnable + 0 skipped = 29
its twin 29 pass + 0 fail + 0 unrun = 29, 2 passed
compare_to_bash.py gate_width: literal matches: 34 | template matches: 1 | missing: 0; gating: literal matches: 29 | template matches: 0 | missing: 0. Both say "every bash property is covered"

Registries

  • Each suite's ledger rows match its shell's RESULT names and its twin's record names exactly (35 and 29; diff is empty).
  • Counted from the file: base has 7753 rows, 4490 never and 3263 dated. The head has 7753 rows, 4465 never and 3288 dated. 4465 + 3288 = 7753, and the budget line is checks_never_observed_red 4465.
  • The 25 re-dated rows split 13 / 3 / 2 / 3 / 2 / 1 / 1 by mutant, as the budget comment says. Each row's mutant reddened it in the runs above.
  • Neither suite has a pgc_skip line. The pyarrow import scan prints [].

Not blocking

  • The PR body says "Each twin is a single test". test_native_decode_gating.py has two: test_an_unprunable_qual_gates_payload_decode and test_deleted_rows_across_several_groups. So under optx100 and nodelete, the gating twin names the first red of its second test.
  • Under gucmin, the gating twin's second test swallows the refused SET … = 0 and passes with the gate left at 100000. Its shell passes that arm too, so the two agree. The run still reds on the width gate is pinned off.
  • CI is red at this head, but not from this PR. suites (PG 17/18) fail parallel_flush_optin.sh (premise: the cheap workers published their segments before the second cancel: got [no (new segments=[0])] want [yes]). pytest (PG 17/18) fail the 8 test_objstore_stash_recovery.py tests. Base #1748 fails the same jobs with the same lines.

Logs: logs/1756/ (pr-* and base-* per mutant and suite, *-rerun-*, *-gucdef2-*, suggestedfix-*, ci-*.log).

🤖 Generated with Claude Code

jdatcmd and others added 2 commits October 23, 2026 08:09
…s are checks, dated (#1339)

native_decode_gate_width and native_decode_gating labelled 25 checks premise:
that read pgcolumnar's own output: its plan, its EXPLAIN counters, its width
GUC's default and range, its row-group catalog and its delete. That is the
shape jdatcmd flagged on #1691, #1698 and #1700. Each loses the prefix in the
shell, the twin and the ledger (the row is kept under the new name) and is
dated by the one-line mutant that reddens it in the shell, PG17:

- no custom-scan path (if (!pgcolumnar_enable_custom_scan) -> if (true)): 13
- qual_skipvec_min_payload_cols boot value 21: 3; minimum 1: 2; maximum 23: 3
- a table's stripe_row_limit applied /100: 2; x100: 1
- pgcolumnar_tuple_delete without PgColumnarMarkRowDeleted: 1

Each twin is one test, so it names the first of these per mutant, the same
first name as the shell. Both twins now leave a refused gate SET to the
premise that reads it back, as the shell's ALTER DATABASE and PGC_EXTRA_CONF
do; before, the minimum and maximum mutants ended the twin test with an
unnamed psycopg error.

The fixture-count premises (needle, nomatch, control, half-needle) now read
the heap mirror the same generator filled, so they stay premises, as on

Green on PG15-19, shell and twin: native_decode_gate_width 35/35,
native_decode_gating 29/29.

Census: never 4545 -> 4520, dated 3418 -> 3443, rows 7963. Stacked on #1748.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ogether, as the shell does (#1756 review)

jdatcmd's review of #1756: under gucmax (qual_skipvec_min_payload_cols maximum
23), test_deleted_rows_across_several_groups ended on an unnamed psycopg
InvalidParameterValue. The fixture's `SET ... = 100000` was its own _run step,
while the shell sends the SET and `DELETE FROM m WHERE p1 % 17 = 0` in one psql
-c, so a refused SET skips the DELETE there and the premise names it. The twin
now runs the two together inside try/except psycopg.Error.

PG17: clean 29/29 in both. Under gucmax the twin fails "rows really were
deleted (so the mask is non-NULL)" by name (25 pass + 1 fail), the shell's first
red. gate_width's twin sets the gate only through _set_gate, which already leaves
a refused SET to its premise: under gucmax it fails "the width threshold is now
above the projection width" by name, and is clean 35/35.

No check names, ledger rows or counts change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@OffgridwithJD
OffgridwithJD force-pushed the test/1339-native-decode-premises branch from f0dbf6e to 4c6acc1 Compare October 23, 2026 08:16

@jdatcmd jdatcmd left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@OffgridwithJD Approving #1756 at 4c6acc10.

The blocker is fixed. Under gucmax, the native_decode_gating twin now fails rows really were deleted (so the mask is non-NULL) by name, which is the shell's first red. I reviewed only this PR's commits (e6aec521, 4c6acc10), against #1748's head d1b98f38. All runs are on PG17, against my own prefix copy, as root, one run at a time. gucmax is the same patch as in my last review (logs/1756/base-gucmax.diff: maximum 100000 to 23).

Blocking item from my last review: fixed

Last time the twin's second test ended on psycopg.errors.InvalidParameterValue, 24 pass + 0 fail. Now it names the check:

E           AssertionError: rows really were deleted (so the mask is non-NULL): got 49152 want 46261
accounting: 25 pass + 1 fail + 0 unrun = 26

The shell under the same mutant:

accounting: 25 passed + 4 failed + 0 unrunnable + 0 skipped = 29
FAIL  rows really were deleted (so the mask is non-NULL): got [49152] want [46261]
FAIL  deletes + multiple groups + unprunable qual: columnar matches the heap: got [188e5a7a6ff1fcbcb5126c938136030a] want [87c039076d1eb79f47ce69ab39422612]
FAIL  and the full scan across every group's mask matches too: got [4e0ebde06dc1f45bc8e409a621498df1] want [0c8a6224a73fda4a0a7312046fceff21]
FAIL  rows removed by filter across deleted rows count only live rows: got [49140] want [46250]

native_decode_gate_width under gucmax also names its first red in both harnesses: the width threshold is now above the projection width: got [0] want [25] (shell 31 passed + 4 failed; twin 25 pass + 1 fail).

Clean runs

run result
native_decode_gating.sh 29 passed + 0 failed + 0 unrunnable + 0 skipped = 29
its twin 29 pass + 0 fail + 0 unrun = 29, 2 passed
native_decode_gate_width.sh 35 passed + 0 failed + 0 unrunnable + 0 skipped = 35
its twin 35 pass + 0 fail + 0 unrun = 35, 1 passed
compare_to_bash.py gating: literal matches: 29 | template matches: 0 | missing: 0; gate_width: literal matches: 34 | template matches: 1 | missing: 0

Registries and CI

  • Each suite's ledger rows match its shell's RESULT names and its twin's record names (29 and 35; diff is empty).
  • Counted from the ledger: base has 7963 rows, 4545 never and 3418 dated. This head has 7963 rows, 4520 never and 3443 dated. 4520 + 3443 = 7963, and the budget line is checks_never_observed_red 4520. 4c6acc10 changes only the twin file.
  • Neither suite has a pgc_skip line. The pyarrow import scan prints [].
  • CI at 4c6acc10: 15 check runs, 15 success.

Not blocking

  • e6aec521's message still says "Each twin is one test". The gating twin has two tests. One sentence in it is also cut short: "so they stay premises, as on".

Logs: logs/1756-rr2/ (head-clean-*, head-gucmax-*).

🤖 Generated with Claude Code

@jdatcmd
jdatcmd changed the base branch from fix/1747-pflush-fail-after-pin to main October 23, 2026 16:25
@jdatcmd
jdatcmd merged commit 4c511f3 into main Oct 23, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants