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
6 changes: 3 additions & 3 deletions test/check_ledger.tsv
Original file line number Diff line number Diff line change
Expand Up @@ -3584,11 +3584,11 @@ projections projections unknown column rejected 15;16;17;18;19 never -
range_pruning range_pruning a clustered overlap near the end skips the four vectors only their upper bounds rule out 15;16;17;18;19 2026-10-13 native_range_excludes never using max_upper (haveUpper forced false)
range_pruning range_pruning a clustered overlap skips vectors instead of decoding them 15;16;17;18;19 never -
range_pruning range_pruning a scattered column skips nothing, which is what the documentation says 15;16;17;18;19 never -
range_pruning range_pruning a unit holding an unbounded range is never pruned from above 15;16;17;18;19 never -
range_pruning range_pruning a unit holding an unbounded range is never pruned from above 15;16;17;18;19 2026-10-17 columnar_write_state.c: the writer ignores an unbounded range, so its unit records only the largest finite upper bound
range_pruning range_pruning a window no row can meet reads no chunk group at all 15;16;17;18;19 2026-10-13 native_range_excludes never using max_upper (haveUpper forced false)
range_pruning range_pruning a window no row can meet returns nothing, from both 15;16;17;18;19 never -
range_pruning range_pruning an empty range contributes no bound, so its units record NULL 15;16;17;18;19 never -
range_pruning range_pruning and an unbounded range records a bound that is present but empty 15;16;17;18;19 never -
range_pruning range_pruning and an unbounded range records a bound that is present but empty 15;16;17;18;19 2026-10-17 columnar_write_state.c: the writer ignores an unbounded range, so its unit records only the largest finite upper bound
range_pruning range_pruning and still returns the right rows 15;16;17;18;19 never -
range_pruning range_pruning and that late overlap returns the heap's rows 15;16;17;18;19 never -
range_pruning range_pruning and the rows themselves match, not only the count 15;16;17;18;19 never -
Expand All @@ -3613,7 +3613,7 @@ range_pruning range_pruning premise: the range column is summarised in several u
range_pruning range_pruning premise: the range type collates differently from its element type 15;16;17;18;19 never -
range_pruning range_pruning so does a containment probe 15;16;17;18;19 never -
range_pruning range_pruning the overlap qual is pushed down, where it used to be dropped 15;16;17;18;19 never -
range_pruning range_pruning the same for a containment probe far to its right 15;16;17;18;19 never -
range_pruning range_pruning the same for a containment probe far to its right 15;16;17;18;19 2026-10-17 columnar_write_state.c: the writer ignores an unbounded range, so its unit records only the largest finite upper bound
recovery recovery premise: s2's INSERT ran in its open transaction before the crash 15;16;17;18;19 never -
recovery recovery s1 aggregate 15;16;17;18;19 2026-10-16 columnar_storage.c: a stripe data page written with no WAL record
recovery recovery s1 col count 15;16;17;18;19 never -
Expand Down
16 changes: 15 additions & 1 deletion test/check_ledger_budget.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3651,4 +3651,18 @@ suites_not_covered 185
# never 3080 + 0 = 3080
# dated 1374 + 1 = 1375
# 3080 + 1375 == 4455
checks_never_observed_red 3080
#
# #1341, range_pruning's unbounded-range arms could not fail (jdatcmd):
# 3080 -> 3077. Both oracle arms summed a payload column their tables lacked,
# so both sides errored and `check` compared "" with "". Fixed, they still
# passed: an empty row in the unit made its minimum empty, which is never
# pruned, and the unbounded row went in by its own INSERT, into a row group
# of its own. With all three fixed, a writer that ignores unbounded ranges
# reddens both arms and "present but empty", on each of 15-19, in both twins.
# No row is added, removed or renamed.
#
# ledger rows 4455 + 0 = 4455
# never 3080 - 3 = 3077
# dated 1375 + 3 = 1378
# 3077 + 1378 == 4455
checks_never_observed_red 3077
63 changes: 37 additions & 26 deletions test/pytest/TESTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -6055,37 +6055,48 @@ Assertion names match.

## 75. test_range_pruning.py: a range prunes on overlap, containment, and under its own collation

SELECT id FROM rp_col WHERE span && tstzrange('2024-03-01', '2024-03-01 06:00')
SELECT count(*) FROM r_col WHERE span && tstzrange('2020-01-02 00:00+00','2020-01-02 01:00+00')

A zone map recorded `minimum` and `maximum` only. Those are the range type's btree
ordering, which sorts by lower bound and then upper bound, so the largest range is
not the one reaching furthest right: a unit holding `[1,2)` and `[3,100)` has the
same `maximum` as one holding `[1,2)` and `[3,4)`. Neither `&&` nor `@>` can be
answered from that, and both read every chunk group. `zone_map.max_upper` records
the greatest upper bound, and the scan skips a unit only when the summary proves it
cannot match.

**THE HEAP IS THE ORACLE.** A pruning defect returns FEWER rows rather than an
error, so every predicate is answered by a heap table built from the same generator
and the row IDENTIFIERS are compared, not the counts.

**THE SCATTERED ZERO IS AN ASSERTION, NOT AN OMISSION.** On a column whose spans are
not clustered, every group holds a bound near the maximum and no summary can exclude
any of them, so `Columnar Chunk Groups Read` equals `Columnar Chunk Groups Total`.

**AND A RANGE COMPARES UNDER THE COLLATION IT WAS DECLARED WITH**, which the type
cache carries as `rng_collation`. Reading the ELEMENT type's `typcollation` instead
made the writer summarise under one ordering and the reader prune under another, and
the scan MISSED ROWS. Not reachable with a built-in range type, so it needs a
user-defined range over a collatable subtype with an explicit non-default
`COLLATION`. Found by @jdatcmd reviewing #1144.

`test_range_pruning` asserts the counters, the heap oracle and the scattered zero.
`test_a_range_prunes_under_its_declared_collation` asserts the collation property,
on a user-defined range type it creates for the purpose.

Independent of `test/range_pruning.sh`: same public seam, own fixture, own row
counts, own observations. Neither file reads or runs the other.
answered from that. `zone_map.max_upper` records the greatest upper bound in three
states: a bound, NULL for no bound (an empty range), and present but empty for an
unbounded range. The scan skips a unit only when the summary proves it cannot match.

The port of `test/range_pruning.sh`, by name, one check per test (#1341, #1339). The
fixture is the shell's: 50,000 rows in one stripe, which is one row group of five
vectors, so pruning shows at both levels. Module fixtures build the tables once.

| test | what it holds |
| --- | --- |
| `test_the_clustered_fixture_holds_the_rows`, `test_the_heap_mirror_holds_the_same_rows` | both tables hold 50,000 rows |
| `test_the_range_column_is_summarised_in_several_units`, `test_every_unit_carries_an_upper_bound`, `test_a_non_range_column_carries_none` | the range column has at least 4 units, each with an upper bound; no other column has one |
| `test_the_columnar_side_returns_the_heaps_rows` | 8 heap-oracle arms, compared by count and two sums: the one-hour, week, point, never-met and late windows, the scattered table, and the two unbounded-range probes |
| `test_the_first_three_oracle_probes_are_not_vacuously_empty` | the window, week and point probes match rows on the heap |
| `test_the_overlap_qual_is_pushed_down`, `test_the_zone_map_is_probed_for_it` | the overlap qual is pushed down and probes the zone map |
| `test_a_clustered_overlap_skips_vectors`, `test_a_containment_probe_skips_vectors` | overlap and containment skip vectors |
| `test_a_late_overlap_skips_the_four_vectors_only_upper_bounds_rule_out` | the late window skips exactly the 4 vectors only upper bounds rule out |
| `test_a_window_no_row_can_meet_reads_no_chunk_group` | the never-met window reads no chunk group |
| `test_a_scattered_column_skips_nothing` | the documented zero |
| `test_the_unbounded_row_really_is_matched` | the unbounded row is matched on the heap |
| `test_the_all_empty_table_is_summarised`, `test_an_empty_range_contributes_no_bound` | the all-empty table is summarised, and records NULL |
| `test_an_unbounded_range_records_a_present_but_empty_bound` | the unbounded unit records a present but empty bound |
| `test_a_discriminating_collation_was_found`, `test_the_collated_pair`, `test_a_collated_overlap_returns_the_heaps_rows` | a range compares under its declared collation: the type's collation differs from text's, the pair holds the same rows and is summarised, four overlaps and the row ids agree with the heap |

**THE UNBOUNDED ARMS COULD NOT FAIL UNTIL #1341.** In the shell, the unbounded tables
lacked the `payload` column the fingerprint sums, so both sides errored and `check`
compared "" with "". Once that was fixed, an empty row in the same unit kept them
green: an empty range sorts first, and a unit whose minimum is empty is never
pruned. And a separate `INSERT` put the unbounded row in a row group of its own.
With all three fixed, a writer that ignores unbounded ranges reddens both arms in
both twins.

**THE COLLATION IS DISCOVERED, NOT ASSUMED.** A host with no collation ordering
differently from the database default declines the collation tests by name.

Independent of `test/range_pruning.sh`: neither file reads or runs the other.

## 76. test_docs_upgrade_chain.py: the documented upgrade chain must be the one that ships

Expand Down
4 changes: 3 additions & 1 deletion test/pytest/expected_tests.txt
Original file line number Diff line number Diff line change
Expand Up @@ -840,4 +840,6 @@ guard_tests 442
# tests) and test_latest_reading.py (1 -> 9 tests), by collection over the cluster files.
# cluster_tests 837 -> 900 for #1339, one check per test in test_native_skip.py (13 -> 76
# tests), by collection over the cluster files.
cluster_tests 900
# cluster_tests 900 -> 932 for #1341, test_range_pruning.py ported by name, one check per
# test (2 -> 34 tests), by collection over the cluster files.
cluster_tests 932
9 changes: 1 addition & 8 deletions test/pytest/test_compare_to_bash.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@
# The shape is `SHELL_REFERENCES`' in `test_harness_deps.py`, asserted in both
# directions for the same reason: a one-way list rots into a permanent exemption.
COMPLETE = ["advisory_lock_class",
"range_pruning",
"alter_table_scans",
"fdw_plan_privilege",
"am_contracts",
Expand Down Expand Up @@ -249,14 +250,6 @@
# having no SKIP verdict for an arm a run chooses not to make, does not carry.
"large_values": "the 1 MB to 200 MB round trips and the TOASTed-update arms are "
"ported; the opt-in chunk-over-1-GB refusal is not",
# The port asserts the engine counters, the heap oracle, the scattered zero
# and the declared-collation property. The shell suite additionally pins the
# three states of max_upper and its own fixture geometry by name, and those
# arms are not ported, so the pair does not grade to zero and is declared
# here rather than listed as complete.
"range_pruning": "counters, heap oracle, scattered zero and the declared "
"collation are ported; the three-state and fixture-geometry "
"premises of the shell suite are not",
# #1137 ported the arms it added and the format-version pin. The rest of the
# shell suite needs a C hook bound at test time (the metapage version) and
# lib.sh's heap-mirror pair, and porting those is #432's work, not this one.
Expand Down
Loading
Loading