Skip to content

test: a NULL key counts as a group in the bucket bound (#1330 review) - #1332

Merged
jdatcmd merged 3 commits into
mainfrom
test/1330-null-group-arm
Oct 13, 2026
Merged

jdatcmd merged 3 commits into
mainfrom
test/1330-null-group-arm

Conversation

@jdatcmd

@jdatcmd jdatcmd commented Oct 13, 2026

Copy link
Copy Markdown
Collaborator

This adds the arm @OffgridwithJD asked for in the #1330 review. It shows that the +1 for NULL in the bucket bound carries weight. The change is tests only.

The arm, in both twins

Rows at 00:00:59, 00:01:30 and 00:02:00 fill the one-minute bucket bound of 3 exactly: floor(61 s / 60 s) + 2. NULL rows add a fourth group. Under groupagg_max_groups = 3:

  • with the NULL group in the bound (main): the path is declined, and all four groups are answered;
  • without it: the path is taken, and the query errors with exceeded pgcolumnar.groupagg_max_groups (3).

The row count can't decline it, because the table has 200 rows (the pytest twin, 160), well over the cap.

Evidence

Red with the NULL group removed (*withNull *= one), on PG17:

  • shell twin: got [ERROR: grouped vectorized aggregate exceeded pgcolumnar.groupagg_max_groups (3)] want [4];
  • pytest twin: the same check fails with the same error.

Green at bd5b734:

  • native_groupagg.sh: 90/90 on 15, 16, 17 and 19 (1 skip where ICU is absent), and 92/92 on 18.
  • test_native_groupagg.py: 19 passed on each of 15–19.
  • test_docs_cover_the_corpus.py: green.

Bookkeeping: TESTS.md gains a row for the new test. cluster_tests goes from 527 to 528 by collection: 947 tests, 419 guard + 528 cluster. None of these suites has ledger rows.

🤖 Generated with Claude Code

@jdatcmd

jdatcmd commented Oct 13, 2026

Copy link
Copy Markdown
Collaborator Author

Pushed abf9fb2. This closes the gap I reported on #1330: the pytest twin missed the bound-without-row-count mutation.

Why it missed: once the mutated gate let the skewed key through, the grouped path lost to the ordinary plan on cost, so nothing errored. Both twins' gate helpers now set enable_hashagg and enable_sort off, so the grouped path wins whenever the gate offers it. The arms now measure the gate, not the cost model. A declined query still runs, on the disabled plan.

PG17, both twins, at this head:

shell pytest
green 90/90 (+1 skip) 19 passed
bound without the row count red at the skewed key red at the skewed key (was green)
no NULL group red at the NULL arm red at the NULL arm
gate on the estimate red: the skewed key, the cached plan red at the skewed key
no gate red: 4 gate arms red: plain key declined, NULL arm

Every mutation is now caught by both twins. Runs on the other majors follow before I ask for review.

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

Approved at bd5b734. Both twins carry the arm, each with its own rows; the shell one is my repro from #1330, and the pytest one uses other second offsets and 40 rows a group. The row count (200 and 160) is well over the cap, so only the NULL group can decline it.

On PG17 at this head: native_groupagg.sh 90 passed + 1 skip (ICU, absent here), and test_native_groupagg.py 19 passed, the new test included. I relied on your red runs for the mutation; I'd seen the same error myself on #1330 with *withNull *= one.

@OffgridwithJD

Copy link
Copy Markdown
Collaborator

A correction to my approval: it says bd5b734, but it attached to abf9fb2, because you pushed while I was running. I've now run abf9fb2 too, on PG17: native_groupagg.sh 90 passed + 1 skip (ICU), and test_native_groupagg.py 19 passed. The approval stands at abf9fb2.

abf9fb2 also turns hash aggregation and sorting off in the gate helpers, so the gate arms measure the gate rather than the cost model. That closes the pytest-twin gap you reported on #1330, where the row-count mutation lost on cost and stayed green. I haven't rerun that mutation myself.

jdatcmd and others added 3 commits October 13, 2026 15:46
@OffgridwithJD's shape: three minute buckets fill the bucket bound exactly,
and NULL rows add a fourth group. Under a cap of 3 a bound without the NULL
group takes the path and errors; with it the path is declined and the four
groups are answered. In both twins.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The bound-without-row-count mutation stayed green in the pytest twin: its
skewed table, once the mutated gate let it through, lost to the ordinary plan
on cost, so nothing errored. Both twins' gate helpers now turn hash
aggregation and sorting off, so the grouped path wins whenever the gate offers
it; a declined query still runs, on the disabled plan.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@jdatcmd

jdatcmd commented Oct 13, 2026

Copy link
Copy Markdown
Collaborator Author

@OffgridwithJD, rebased onto 614eb02 (#1331) at e4ca93b. The only conflict was expected_tests.txt: #1331 also took 527 → 528, so this is now 528 → 529, confirmed by collection (948 tests: 419 guard + 529 cluster). test_docs_cover_the_corpus.py 40 passed. native_groupagg.sh on PG17 at this head is above. The tests are unchanged from abf9fb2. Please re-approve at e4ca93b.

@jdatcmd
jdatcmd force-pushed the test/1330-null-group-arm branch from abf9fb2 to e4ca93b Compare October 13, 2026 15:47
@jdatcmd

jdatcmd commented Oct 13, 2026

Copy link
Copy Markdown
Collaborator Author

@OffgridwithJD, a small ask: your approval is at abf9fb2, and the head is e4ca93b after the rebase onto #1331. The only change is expected_tests 528 → 529, and CI is 15/15. I merge only on an approval at the head, so please re-approve when you can.

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

Re-approved at e4ca93b. I checked per-file patch identity against abf9fb2, which was on b55cb23. native_groupagg.sh, test_native_groupagg.py and TESTS.md are identical. expected_tests.txt differs only by the restatement 528 → 529 on 614eb02.

@jdatcmd

jdatcmd commented Oct 13, 2026

Copy link
Copy Markdown
Collaborator Author

Merging: approved by @OffgridwithJD at e4ca93b, and CI is green on that head.

@jdatcmd
jdatcmd merged commit a30faeb into main Oct 13, 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