From 2c7cf896c167b1049414fa6668502c8a85d3ab46 Mon Sep 17 00:00:00 2001 From: "Joshua D. Drake" Date: Thu, 22 Oct 2026 05:37:03 +0000 Subject: [PATCH 01/46] test: native_fold_deferral gains its pytest twin, and a mirror copied from the table under test no longer vouches for it (#1341) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ports test/native_fold_deferral.sh to test/pytest/test_native_fold_deferral.py, all 13 checks by the shell's name, one test per check, on a private database. Three holes in the shell, each measured red-first on PG17 against main and fixed in both harnesses: - The heap mirror was `CREATE TABLE fd_heap AS SELECT * FROM fd_t`, read through the scan under test. With the writer storing one p1 value off by one (writecorrupt), the fold, the row path and the mirror all agreed: main passed 13/13. The mirror is now written from the generator, and writecorrupt reddens both heap-mirror checks in both twins. - The answers were read with `psql ... | tail -1` and compared with check. With the server stopped before the correctness section, all three answer checks passed on empty strings. Readings now run with ON_ERROR_STOP and come back empty on failure; the checks are check_text, and the counts go to check_num raw instead of defaulting to 0. Stopped server: only the three checks read before the stop pass. - Three premises are reddened by a one-line pgcolumnar change: the fold's ANALYZE line (foldno), the EXPLAIN group total (badtotal), the writer's stripe limit (nostripe). They lost the prefix (#1339). Eight mutations on PG17 each redden the same names in shell and twin: nodefer 3, deferall 1, nocursor 3, dropsurv 4, writecorrupt 2, foldno 1, badtotal 2, nostripe 1. Head is 13/13 in both on each of 15-19. Seeded: 13 rows, 9 dated, 4 never. Census 4192 -> 4196 never, 2952 -> 2961 dated, 7157 rows; suites_not_covered 69 -> 68; cluster_tests 3416 -> 3429; TESTS.md §301. Co-Authored-By: Claude Opus 5.5 --- test/check_ledger.tsv | 13 ++ test/check_ledger_budget.txt | 24 ++- test/native_fold_deferral.sh | 51 ++++-- test/pytest/TESTS.md | 25 +++ test/pytest/expected_tests.txt | 4 +- test/pytest/test_compare_to_bash.py | 1 + test/pytest/test_native_fold_deferral.py | 207 +++++++++++++++++++++++ test/pytest/unported_suites.txt | 1 - 8 files changed, 307 insertions(+), 19 deletions(-) create mode 100644 test/pytest/test_native_fold_deferral.py diff --git a/test/check_ledger.tsv b/test/check_ledger.tsv index 8b086cb67..ee4ca13be 100644 --- a/test/check_ledger.tsv +++ b/test/check_ledger.tsv @@ -4009,6 +4009,19 @@ native_fetch_sort_context native_fetch_sort_context premise: the columnar side i native_fetch_sort_context native_fetch_sort_context three-key merge cond with a filter (#720) 15;16;17;18;19 2026-10-22 columnar_tableam.c: the deferred decode builds its needed-column set in the caller's context and bms_free's it there (#720 reverted, first half) native_fetch_sort_context native_fetch_sort_context two-key merge join count is right (#720) 15;16;17;18;19 2026-10-22 columnar_tableam.c: the deferred decode builds its needed-column set in the caller's context and bms_free's it there (#720 reverted, first half) native_fetch_sort_context native_fetch_sort_context two-key merge join sum(id) is right (#720) 15;16;17;18;19 2026-10-22 columnar_tableam.c: the deferred decode builds its needed-column set in the caller's context and bms_free's it there (#720 reverted, first half) +native_fold_deferral native_fold_deferral EXPLAIN group total == stats() group count 15;16;17;18;19 2026-10-22 columnar_vector.c: the fold counts each group twice in its total +native_fold_deferral native_fold_deferral adaptive: eager groups load more than survivors alone (work measured, not assumed) 15;16;17;18;19 never - +native_fold_deferral native_fold_deferral adaptive: only the optimistic first group deferred at 99% survival 15;16;17;18;19 2026-10-22 columnar_vector.c: the fold never defers, so the payload is always loaded eagerly +native_fold_deferral native_fold_deferral deferred fold == heap mirror (selective) 15;16;17;18;19 2026-10-22 columnar_vector.c: a deferred group's first survivor is never folded +native_fold_deferral native_fold_deferral deferred fold == row path (selective) 15;16;17;18;19 2026-10-22 columnar_vector.c: a deferred group's first survivor is never folded +native_fold_deferral native_fold_deferral deferred: every group deferred at 1% survival 15;16;17;18;19 2026-10-22 columnar_vector.c: the fold never defers, so the payload is always loaded eagerly +native_fold_deferral native_fold_deferral deferred: loads == survivors x payload width (the identity) 15;16;17;18;19 2026-10-22 columnar_vector.c: a deferred group's first survivor is never folded +native_fold_deferral native_fold_deferral fold == heap mirror (high survival) 15;16;17;18;19 2026-10-22 columnar_vector.c: a deferred group's first survivor is never folded +native_fold_deferral native_fold_deferral premise: the predicate has survivors (the identity is not 0 == 0) 15;16;17;18;19 never - +native_fold_deferral native_fold_deferral premise: the selective predicate is genuinely selective (<= 2%) 15;16;17;18;19 never - +native_fold_deferral native_fold_deferral the fixture spans several groups 15;16;17;18;19 2026-10-22 columnar_write_state.c: the writer ignores a table's stripe_row_limit +native_fold_deferral native_fold_deferral the loads counter exists under ANALYZE 15;16;17;18;19 never - +native_fold_deferral native_fold_deferral the selective query takes the fold (ANALYZE, post-#602) 15;16;17;18;19 2026-10-22 columnar_vector.c: the Columnar Batch Fold ANALYZE line reports no native_fold_skipguard native_fold_skipguard and reports the 58 it skipped, on its own plan (#542) 15;16;17;18;19 2026-10-22 columnar_reader.c: the fold path stops counting the vectors decode skipped (#542 regression) native_fold_skipguard native_fold_skipguard and the scalar arm agrees 15;16;17;18;19 never - native_fold_skipguard native_fold_skipguard so decoded plus skipped is the group's vectors, on the FOLD arm 15;16;17;18;19 2026-10-22 columnar_reader.c: the fold path stops counting the vectors decode skipped (#542 regression) diff --git a/test/check_ledger_budget.txt b/test/check_ledger_budget.txt index dbcc9fa8f..d03086420 100644 --- a/test/check_ledger_budget.txt +++ b/test/check_ledger_budget.txt @@ -15,7 +15,7 @@ # # Adding a check to a suite that is already covered does not move it, which is # what makes it safe to bound. -suites_not_covered 69 +suites_not_covered 68 # # checks_never_observed_red -- A CENSUS. NOT a ceiling, and it must not become # one. @@ -6112,4 +6112,24 @@ suites_not_covered 69 # never 4176 + 16 = 4192 # dated 2914 + 38 = 2952 # 4192 + 2952 == 7144 -checks_never_observed_red 4192 +# +# #1341, native_fold_deferral seeded, with its twin (OffgridwithJD): 4192 -> 4196, and +# suites_not_covered 69 -> 68. The suite had no rows. It enters with 13 checks from +# five-major runs (all green on each of 15-19, shell and twin alike), after three fixes. +# The heap mirror was a CTAS from the columnar table, so it read through the scan under +# test: with the writer storing one value off by one, the fold, the row path and the +# mirror all agreed and main passed 13/13. The mirror is now written from the +# generator. The three answer checks compared readings taken with tail -1 using check: +# with the server stopped all three passed on empty strings. Readings now fail empty +# and the checks are check_text. And three premises a one-line pgcolumnar change +# reddens (the fold's ANALYZE line, the EXPLAIN group total, the writer's stripe limit) +# lost the prefix. 9 are dated by eight mutations, each reddening the same names in +# both twins. 4 arrive never: the two remaining premises, the counter's presence, and +# the eager-load bound, which no measured mutation reaches: with every group deferring, +# the loads equal survivors times width exactly, which still clears its 99% floor. +# +# ledger rows 7144 + 13 = 7157 +# never 4192 + 4 = 4196 +# dated 2952 + 9 = 2961 +# 4196 + 2961 == 7157 +checks_never_observed_red 4196 diff --git a/test/native_fold_deferral.sh b/test/native_fold_deferral.sh index 1df9062fd..2d7b0810c 100755 --- a/test/native_fold_deferral.sh +++ b/test/native_fold_deferral.sh @@ -29,9 +29,23 @@ GUC="SET pgcolumnar.enable_ungrouped_vector_agg = on;" NP=4 # payload columns ROWS=120000 -sq() { # scalar under this suite's GUC; tail -1 drops the SET tag - env PATH="$PGC_BINDIR:$PATH" psql -h 127.0.0.1 -p "$PGC_PORT" -U postgres \ - -d "$PGC_DB" -At -c "$GUC $1" 2>/dev/null | tail -1 +# sq SQL -> the result under this suite's GUC, or nothing when the statement failed. +# It used to be `... | tail -1`, which read a failed statement as whatever line came +# last; with ON_ERROR_STOP and the exit status, a failure is empty, and every +# comparison below refuses an empty side (check_text) or a non-number (check_num). +sq() { + local out + out="$(env PATH="$PGC_BINDIR:$PATH" psql -h 127.0.0.1 -p "$PGC_PORT" -U postgres \ + -d "$PGC_DB" -At -v ON_ERROR_STOP=1 -c "$GUC $1" 2>/dev/null)" || return 0 + tail -1 <<<"$out" +} +# rq SQL -> the result on the row path (the vectorized aggregate off), the same way. +rq() { + local out + out="$(env PATH="$PGC_BINDIR:$PATH" psql -h 127.0.0.1 -p "$PGC_PORT" -U postgres \ + -d "$PGC_DB" -At -v ON_ERROR_STOP=1 \ + -c "SET pgcolumnar.enable_ungrouped_vector_agg = off; $1" 2>/dev/null)" || return 0 + tail -1 <<<"$out" } explain_line() { # explain_line