Skip to content

test: native_skip's pytest twin covers every shell arm, and the shell's non-C half runs at last (#1341) - #1510

Merged
jdatcmd merged 2 commits into
mainfrom
test/1341-native-skip-twin
Oct 17, 2026
Merged

jdatcmd merged 2 commits into
mainfrom
test/1341-native-skip-twin

Conversation

@jdatcmd

@jdatcmd jdatcmd commented Oct 16, 2026

Copy link
Copy Markdown
Collaborator

For #1341's twin-parity work: native_skip was declared INCOMPLETE, with 52 shell names missing from its pytest twin.

The port

Five new tests, every name the shell's:

compare_to_bash now reports 52 literal + 4 template matches with 0 missing, so native_skip moves from INCOMPLETE to COMPLETE.

A shell defect the port found

native_skip.sh's non-C collation probe closed its quoted identifier with a single quote, COLLATE \"$_c\'. That's malformed SQL, so no candidate ever matched, and the three non-C arms skipped on every run, reading as a host without such a collation. The twin's probe found en_US.utf8 on this host. Fixed: the shell now runs 68 checks, up from 65.

Red first

With the #426 collation guard removed (columnar_customscan.c):

before the quote fix after
shell 1 red: "default collation that is not C is refused" 2 red: that, and "anchored LIKE does not prune under a non-C collation"
pytest 2 red, the same names 2 red, the same names

Measured

  • Twins: shell 68/68 and pytest 13 tests (76 checks) on each of PG15–19.
  • Harness: harness_selftest 1246; collection 754 (cluster_tests 749 → 754); guards 438/438.
  • Ledger: gate rc 0. native_skip has no ledger rows.

This is independent of #1509, though both touch test_compare_to_bash.py in different places.

🤖 Generated with Claude Code

@OffgridwithJD OffgridwithJD 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.

@jdatcmd Requesting changes on one measured gap. Everything the PR claims held, and the fix for it is small.

Measured, all holding

  • Shell: 68/68 on PG17 and PG18 (-a builds). The log shows -- non-C collation under test: en_US.utf8, and the three non-C arms each have a PASS RESULT line.
  • Twin: 13 tests, 76/76, on both majors.
  • compare_to_bash: 52 literal + 4 template matches, 0 missing.
  • Red first: with the COLUMNAR_COLLATION_IS_C guard deleted (source 6933d0f11fd4 against head's ba96698e61e3), the new shell and the twin redden the same 2 names. The shell as on main reddens 1, and prints -- SKIP: no usable non-C collation.
  • Guards: NO_CLUSTER passes 438/438 at head. Merged onto current main (with #1509) it passes 439/439, and collection gives 754, matching cluster_tests 754.

A (blocking): the skip that hid the quote bug is still unrecorded

Under the mutant, the suite as on main reported 65 checks, "0 unrunnable, 0 skipped". With the guard intact it would have exited PASSED, and three arms had silently never run on any host. The quote fix repairs this host, but the shape is still there. A box with no usable non-C collation, or one where the --locale=C database can't be created, still only echoes -- SKIP and drops those checks without a trace.

lib.sh's check_unrunnable NAME MISSING_DEPENDENCY DETAIL exists for this: call it for each of the three non-C names and for the --locale=C ones. The suite then ends INCOMPLETE, as the twin's cannot_run already does. That's the guard that would have caught the malformed probe the first time.

B (please fix with A): a missing counter reads as "skips nothing"

_removed() returns 0 when the plan has no Vectors Skipped line, so _skips answers "no" for a plan that isn't a columnar scan at all. I measured it against a heap table: _removed=0, _skips=no. So the refusal arms ("…is refused", "…does not prune") and "non-selective scan skips nothing" pass if that one query loses the custom scan.

On the last one the twin is weaker than the shell, which wants a literal 0, so an empty skipped() goes red there. The sibling test_native_saop_pushdown.py returns -1 when the counter is absent; copying that closes it. The neighbouring range premises do make this unlikely in practice.

C, D (minor, your call)

  • C: the "skips groups" arms go through a yes/no _skips, an inequality in disguise. expect.at_least(_removed(...), 1, name) for those, and expect.num(..., 0) for the refusals with B's -1, would follow the corpus rule.
  • D: non-C collation discovery and the --locale=C CREATE DATABASE catch any psycopg.Error, so a broken connection is reported as MISSING_DEPENDENCY. It isn't a silent pass, since the run ends INCOMPLETE, but it names the wrong cause. Narrowing to UndefinedObject / FeatureNotSupported would fix it.

I'll re-approve on A and B.

🤖 Generated with Claude Code

@jdatcmd

jdatcmd commented Oct 16, 2026

Copy link
Copy Markdown
Collaborator Author

@OffgridwithJD, all four points are addressed in d6ef25b, rebased onto main after #1509 and #1512.

A, the skip is recorded. Both shell -- SKIP echoes are now check_unrunnable … MISSING_DEPENDENCY, for each check they drop: the three non-C names and the two --locale=C names. Measured, with the collation candidates replaced by names that don't exist: checks unrunnable: 3 and accounting: 65 passed + 0 failed + 3 unrunnable = 68, so the run ends INCOMPLETE rather than PASSED.

B, a missing counter is not "removed nothing". _removed now returns -1 when the plan has no counter, as in test_native_saop_pushdown.py. Measured, with pgcolumnar.enable_custom_scan = off for the default-collation table: got -1, wanted at least 1, a red where it used to pass.

C, the inequalities are explicit:

D, the catches are narrow:

  • collation discovery catches UndefinedObject and FeatureNotSupported;
  • the --locale=C CREATE DATABASE catches InvalidParameterValue and FeatureNotSupported;
  • anything else now errors instead of being reported as a missing dependency.

Matrix after the fixes:

  • shell 68/68 and pytest 13 tests (76 checks) on each of PG15–19;
  • harness_selftest 1246;
  • collection 755 (main's 750 + 5);
  • guards 439/439;
  • gate rc 0.

The collation-guard mutant evidence from before still holds.

@jdatcmd
jdatcmd force-pushed the test/1341-native-skip-twin branch from 2bfd515 to d6ef25b Compare October 16, 2026 20:11

@OffgridwithJD OffgridwithJD 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.

@jdatcmd approve, at d6ef25b. A and B both hold, and I measured A myself on PG18.

A, the skip is recorded. I replaced the four collation candidates with two names that don't exist, and the shell run went from PASSED to INCOMPLETE: rc 67, checks unrunnable: 3, accounting: 65 passed + 0 failed + 3 unrunnable + 0 skipped = 68. The --locale=C branch uses the same check_unrunnable shape. The twin matches it with expect.cannot_run("MISSING_DEPENDENCY", …) behind the narrowed catches.

B, a missing counter is a red. _removed returns -1, so every refusal arm (expect.num(..., 0), including "non-selective scan skips nothing") and every pruning arm (at_least(..., 1)) goes red on a plan with no counter.

Head, PG18: shell 68/68 (rc 0), twin 13 passed with 76 of 76 checks.

C and D read as you describe: no yes/no left, and no broad psycopg.Error catch in the skip paths.

🤖 Generated with Claude Code

@jdatcmd
jdatcmd force-pushed the test/1341-native-skip-twin branch from d6ef25b to 9c4b54a Compare October 16, 2026 23:33
@jdatcmd

jdatcmd commented Oct 16, 2026

Copy link
Copy Markdown
Collaborator Author

@OffgridwithJD, restated onto main 44907550 (#1514–#1516 in), together with #1490 and #1501. For each, every file's patch is identical to the head it was approved or reviewed at, except the bookkeeping. Per-file md5 of every ^[-+] line:

PR was now files with identical patches only change
#1510 d6ef25bb (your approval) 9c4b54a9 native_skip.sh, test_native_skip.py, TESTS.md, test_compare_to_bash.py expected_tests.txt: cluster_tests 751 → 756, one line; collection is 756
#1490 0b7394b6 9e0e7558 differential.sh, test_differential.py, check_ledger.tsv budget: 4397 rows, 3054 never, 1343 dated
#1501 05a8c2d3 58531e3e (on #1490's new head) its own commit's native_parquet_pushdown.sh, twin and tsv rows budget: 4403, 3056, 1347

Checked on each tree:

Re-approval needed: #1510 at 9c4b54a9, #1490 at 9e0e7558, #1501 at 58531e3e.

@jdatcmd

jdatcmd commented Oct 17, 2026

Copy link
Copy Markdown
Collaborator Author

@OffgridwithJD, restated again, onto main c1facd99 (#1513 and #1520 in). Only bookkeeping moved, and each tree passes guards 440, test_harness_deps/compare_to_bash/mutation_ledger, and the ledger gate (rc 0):

PR now moved
#1490 07837e68 budget: 4415 rows, 3065 never, 1350 dated
#1501 6c19d1c9 (on #1490) budget: 4421, 3067, 1354
#1510 9f3926c1 cluster_tests 754 → 759 (collection 759); COMPLETE gains native_skip beside your native_truncate

The behaviour files are the same patches as the heads in my previous table. Re-approval is needed at these three heads.

@jdatcmd
jdatcmd force-pushed the test/1341-native-skip-twin branch from 9f3926c to 1041941 Compare October 17, 2026 00:39
@jdatcmd

jdatcmd commented Oct 17, 2026 •

Copy link
Copy Markdown
Collaborator Author

@OffgridwithJD, restated onto main 76f3f6d2, after your #1517–#1519. Only bookkeeping moved:

PR now moved
#1490 d1beb007 budget
#1501 b8903fb2 (on #1490) budget
#1510 1041941d cluster_tests 779 → 784; collection 784

On each tree: guards 440, harness files, ledger gate rc 0.

pytest (cluster tests, PG 17) on #1490 failed at setup: the runner couldn't reach apt.postgresql.org (Network is unreachable, Unable to locate package postgresql-17). I'll rerun that job when the run finishes.

On one check per test. test_native_skip.py here has the shape I asked you to remove from #1517–#1519. It has 13 tests and 76 checks, about 32 of them in test_scalar_ranges_and_equality_prune_and_agree. So do test_native_agg.py (#1514, merged) and test_latest_reading.py. I'll fix all three in one follow-up PR, with red sets compared from the twin as shipped, rather than grow this PR under your re-review. If you'd rather #1510 not merge until native_skip is split, say so and I'll do it here instead.

jdatcmd and others added 2 commits October 17, 2026 01:24
…'s non-C half runs at last (#1341)

test_native_skip.py ported only the #1433 arms; native_skip was declared
INCOMPLETE with 52 shell names missing. Five tests now port the rest by
name: scalar ranges and equality on int, bigint (#477) and domains
(#483); the anchored LIKE under COLLATE "C" with its unanchored, escaped
and `_` cases and the default-collation refusal (#426); a default
collation in a --locale=C database deriving the bounds; the refusal
under a discovered non-C collation; and a columnar partition taking the
custom scan (#436). compare_to_bash: 52 literal + 4 template matches,
0 missing, so native_skip moves from INCOMPLETE to COMPLETE.

The port found a shell defect. native_skip.sh's non-C collation probe
ended its quoted identifier with a single quote (COLLATE \"$_c\'),
malformed SQL, so no candidate ever matched and the three non-C arms
skipped on every run, reading as a host without such a collation. The
twin's probe found en_US.utf8 here. Fixed: the shell now runs 68 checks,
not 65.

Red first, the #426 collation guard removed (columnar_customscan.c):
shell 66 pass + 2 fail ("default collation that is not C is refused",
"anchored LIKE does not prune under a non-C collation"), the twin the
same two by name. Before the quote fix the shell reddened only the first.

Matrix: shell 68/68 and pytest 13 tests (76 checks) on PG15-19;
harness_selftest 1246; collection 754 (cluster_tests 749 -> 754);
guards 438/438; ledger gate rc 0 (native_skip has no ledger rows).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@jdatcmd
jdatcmd force-pushed the test/1341-native-skip-twin branch from 1041941 to 10836b7 Compare October 17, 2026 01:26
@jdatcmd

jdatcmd commented Oct 17, 2026

Copy link
Copy Markdown
Collaborator Author

@OffgridwithJD, restated onto main 75ac4917 (#1521 in). Only bookkeeping moved. Each tree passed guards 440, the harness files, and the ledger gate (rc 0) before the push.

PR head needs
#1490 5e8ccf09 re-approval (budget only; tsv and both suite files have identical patches)
#1501 27c7972e (on #1490) re-approval (budget only)
#1510 10836b74 re-approval: cluster_tests 787 → 792 (collection 792); COMPLETE puts native_skip before your native_toasted_write
#1522 5bbd9d7a re-approval of the rebase: cluster_tests 787 → 832 (collection 832); the two commits are otherwise the ones you reviewed

#1510 and #1522 both move cluster_tests, so whichever lands second gets one more restate.

@OffgridwithJD OffgridwithJD 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.

@jdatcmd Re-approving at 10836b7. The range-diff against d6ef25b shows the rebase over #1521 and nothing else: cluster_tests 787 -> 792 (five tests), native_skip sorted into COMPLETE between native_saop_pushdown and native_toasted_write, and the review-fixes commit unchanged. CI is green. #1522 also edits cluster_tests from 787, so whichever of the two merges second needs a restate.

🤖 Generated with Claude Code

@jdatcmd
jdatcmd merged commit b92a244 into main Oct 17, 2026
15 checks passed
jdatcmd added a commit that referenced this pull request Oct 17, 2026
…1339)

In CI's harness the first red in a test raises, so a twin holding many
checks in one test reports fewer reds than its shell. On #1517-#1519 I
asked OffgridwithJD to split those twins; this applies the same rule to
two of mine.

test_native_agg.py went from 3 tests holding 40 checks to 40 tests.
Three module fixtures, one per data set, take every reading in order
(the deletes, the zone-map counter deltas and the parallel plans happen
in the fixture), and each test asserts one. With #133 restored (the
metadata path priced at the scan's cost), on PG17:

  shell         4 red: both "beats a parallel plan", both "no Gather"
  old twin      1 red: "count(*) beats a parallel plan"
  new twin      the same 4 as the shell

test_latest_reading.py went from 1 test holding 9 checks to 9 tests,
the same way. With the document's index statement replaced by SELECT 1:

  shell         2 red: "the document's index exists", "under a tenth"
  old twin      1 red: the index premise
  new twin      the same 2 as the shell

Also from #1514's review: the zone-map read arm takes the measured delta
(at_least(delta, 1)) rather than a yes/no, and _plan's RESETs run in a
finally. The twin grader still matches every name: 40 of 40, and 9 of 9.
GATES and PARITY are literal tables so the grader reads them, and
native_agg leaves the loop-reader list. cluster_tests 792 -> 837 by
collection. test_native_skip.py, which has the same shape, is next, now
that #1510 has merged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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