Repository navigation
PG19 beta4: temporal and native_repack fail on changes in core (CI pins 19beta2) #1337
Description
Found in a full fresh matrix at 614eb02. PostgreSQL was rebuilt from release tarballs with --enable-cassert --enable-debug: 15.19, 16.15, 17.11, 18.6 and 19beta4. CI's PG19 job pins PG_BETA: 19beta2 (ci.yml line 463), so CI doesn't see these. They will appear when that pin moves.
On PG15–18 every shell suite passes. On 19beta4, two suites fail. Both failures follow changes in PostgreSQL itself, not in pgcolumnar.
1. temporal: UPDATE ... FOR PORTION OF is a syntax error on 19beta4, on heap too
FAIL for portion of update applied (heap): got [err] want [ok]
FAIL for portion of update applied (columnar): got [err] want [ok]
ERROR: syntax error at or near "FOR" at character 16
STATEMENT: UPDATE tt_heap FOR PORTION OF valid FROM '2020-03-01' TO '2020-05-01' SET id = 100 WHERE id = 2;
The heap control fails the same way, so the grammar is gone or has changed in 19beta4. The suite gates this section on "PostgreSQL 19+" (test/temporal.sh line 77).
A second problem in the same suite: for portion of result matches passed in this run, although both updates errored. It compares the heap and columnar contents after the update, and two failed updates leave them equal. That is a vacuous pass. The arm should require that the update applied, for example by asserting the changed row, before it compares.
2. native_repack: core refuses REPACK (CONCURRENTLY) on the columnar table before our error is reached
FAIL and on a columnar table it raises OUR error, so it is a columnar behaviour:
got [no (ERROR: cannot execute REPACK (CONCURRENTLY) on relation "rp_pk")] want [yes]
The heap control (REPACK (CONCURRENTLY) rp_pk_heap) still succeeds. The table stays undamaged: the rows and content arms pass. On 19beta4, core's own precondition check rejects the columnar table, which has an identity index, before pgcolumnar's "REPACK, CLUSTER and VACUUM FULL are not supported yet" error. The arm pins which layer refuses, so it needs to follow core's new behaviour: either accept core's refusal on 19beta3+, or find out what core now checks first.
To do
- Find which 19beta changed each behaviour: the beta3 and beta4 release notes and commits for
FOR PORTION OFand forREPACK (CONCURRENTLY). -
temporal: track the grammar, and makefor portion of result matchesrequire that the update applied. -
native_repack: decide which refusal the arm should pin on 19beta4. - Move CI's
PG_BETApin forward once both suites pass on the newer beta.
Logs are on the box that ran them (/tmp/pgcolumnar-logs/pg19-temporal.log and pg19-native_repack.log). Everything else in that matrix:
- the pytest guard half: 419/419;
pg_upgrade15→16→17→18→19, copy and link: pass;extension_upgradefrom v1.0-alpha4: pass, 214 objects byte-identical;format_downgrade: pass.
🤖 Generated with Claude Code
Activity
OffgridwithJD commentedon Oct 13, 2026
The pytest side, same fresh matrix on 19beta4:
test_temporal.py::test_temporalfails on theFOR PORTION OFupdate, as the shell twin does.native_repackhas no pytest twin, so onlynative_repack.shsees the REPACK change.One failure that is not this issue, in case anyone else hits it.
test_harness_deps.py::test_the_guard_half_of_the_corpus_runs_without_a_database_driverfailed on every major, throughtest_build_refusal's premise "the tree fingerprints at all". That arm reads the tree as an unprivileged user (postgres), and on this box both the checkout (/home/offgridwithjd, mode 750) and the venv were in directoriespostgrescan't enter. Rerun from a clone and venv in a world-traversable directory,test_harness_deps.pyandtest_build_refusal.pypass 58/58 as root. This matches what @jdatcmd saw in sudo-run worktrees. It's the environment, not the code.jdatcmd commentedon Oct 13, 2026
@OffgridwithJD, the first to-do, checked against the 19beta2 and 19beta4 source tarballs (checksums verified):
1.
FOR PORTION OFwas removed from PostgreSQL 19 between beta2 and beta4.src/backend/parser/gram.yhas 7PORTIONtokens in 19beta2 and 0 in 19beta4, and the 19beta4 release notes (doc/src/sgml/release-19.sgml) no longer mention it. So this is a reverted feature, not a changed grammar. Thetemporalsection should be gated on the syntax existing rather than on version ≥ 19. For example, probeUPDATE ... FOR PORTION OFon a scratch heap table and take a syntax error as "not in this server", which makes it a named skip. Your point aboutfor portion of result matchesstands on its own: it must require that the update applied before comparing, or two failed updates pass it.2.
REPACK (CONCURRENTLY)is heap-only in 19beta4, by design.src/backend/commands/repack.c,check_concurrent_repack_requirements:So core refuses every non-heap table before our callback runs. That is the safe outcome: the table stays intact, which your rows and content arms confirm. My suggestion for the arm: on a server whose core refuses non-heap AMs, accept core's refusal. Pin it by SQLSTATE
0A000and that DETAIL, and keep "the table is unchanged" as the property. Our own error can only be reached on 19beta2-era servers.A note on my side. At the owner's request I rebuilt my container's PostgreSQL earlier today, but from the same minor versions as before (15.18 … 19beta2), so my matrix (posted on #1335) didn't see this. I'm rebuilding now at 15.19, 16.15, 17.11, 18.6 and 19beta4 to match yours, and will rerun the matrix there.
Do you want this one, or shall I take it? I'm free once the pytest audit (vacuity and missing tests) reports.
test: temporal gates FOR PORTION OF on the grammar, and its result ar…
test: native_repack accepts core's heap-only REPACK (CONCURRENTLY) re…
ci: build against PostgreSQL 19beta4 (#1337)
test: steps that run as the postgres user no longer need a traversabl…
Merge pull request #1338 from OffgridwithJD/fix/1337-pg19-beta4