Concepts covered
PostgreSQL 19 - what changes and what breaks
PostgreSQL 19 is the next major release, and as of the last review it is still in beta: Beta 4 shipped on 24 September 2026 and no release candidate had followed it. The release notes carry a placeholder date (“2026-??-??”), so treat everything here as the documented beta behaviour, not shipped behaviour. Each feature area below links to a page that goes deep; the upgrade breakers at the end are the part to read before touching a cluster.
REPACK
19 adds a core REPACK command that rewrites a table to reclaim space or restore order - the
functionality of VACUUM FULL and CLUSTER in one. REPACK CONCURRENTLY does it without blocking
reads and writes, replacing the trigger-and-log-table trick of the pg_repack extension with
logical decoding over a replication slot. It needs wal_level = logical for the concurrent mode.
REPACK covers both modes, the lock behaviour, and
when the extension is still the answer on older releases.
Replication and the WAIT command
WAIT FOR LSN lets a session pause until a standby has written, flushed or replayed WAL up to a
point - the primitive that makes read-your-writes work on asynchronous replicas, with a timeout and
NO_THROW for the fallback path. Logical replication gains sequence support (FOR ALL SEQUENCES, REFRESH SEQUENCES, pg_get_sequence_data()), an EXCEPT table list on
FOR ALL TABLES publications, retain_dead_tuples with max_retention_duration for conflict
detection, CREATE SUBSCRIPTION ... SERVER using postgres_fdw connection parameters,
per-subscription wal_receiver_timeout, and wal_sender_shutdown_timeout. wal_level = replica
can now enable logical capability automatically, reported through effective_wal_level.
Replication and WAIT walks a worked
read-your-writes timeline and all of it.
Autovacuum
Two changes that matter independently. Index vacuuming can run in parallel workers now, governed
by autovacuum_max_parallel_workers and a per-table autovacuum_parallel_workers storage
parameter. And the order in which autovacuum picks tables changed from catalogue order to a
five-component score - freeze age, multixact age, update/delete and insert dead tuples, and analyse
staleness - each with a weight GUC. pg_stat_autovacuum_scores shows the reasoning, and the
wraparound warning fires at 100 million remaining IDs instead of 40 million.
Autovacuum has a worked tuning example.
Plan advice
pg_plan_advice is a core extension that describes planner decisions as a text mini-language -
scan methods, join orders, join methods, partitionwise and parallel choices - which you edit and
feed back to constrain the plan, with EXPLAIN feedback reporting whether each piece of advice
matched. pg_stash_advice stores those strings keyed by query ID and applies them automatically.
It is the in-core answer to plan instability, and it is not pg_hint_plan: advice can only select
between plans the planner would have considered.
Plan advice shows a before/after plan and the
failure modes.
Asynchronous I/O
The I/O subsystem that arrived in 18 gets an elastic worker pool: io_method = worker now scales
between io_min_workers (2) and io_max_workers (8), retiring idle workers after
io_worker_idle_timeout (1 minute) and launching no faster than io_worker_launch_interval
(100 ms), replacing the fixed io_workers count. Read-ahead scheduling improves for large
requests, and EXPLAIN (ANALYZE, IO) reports prefetch queue depth, request counts, waits and
average concurrency per scan node, with auto_explain.log_io for logged plans.
Asynchronous I/O has the details.
Monitoring
pg_stat_lock accumulates lock-type contention (waits over deadlock_timeout),
pg_stat_recovery reports standby replay and promotion state, pg_stat_wal gains
wal_fpi_bytes, pg_stat_replication_slots gains mem_exceeded_count and slot-sync skip columns,
and stats_reset columns spread across the table, index, sequence, function and conflict views.
log_min_messages accepts a per-process-type type:level syntax, log_lock_waits is on by
default, log_autoanalyze_min_duration splits analyse logging out, and new wait events cover COPY
I/O, commit_delay, WAL LSN waits and the WAIT command.
Monitoring is a runbook of the queries.
SQL and COPY
INSERT ... ON CONFLICT DO SELECT [FOR UPDATE] returns the conflicting row instead of writing
over it - an idempotent get-or-create. Window functions gain IGNORE NULLS/RESPECT NULLS on
lead, lag, first_value, last_value and nth_value. COPY adds a JSON output format with
FORCE_ARRAY, ON_ERROR SET_NULL for bad input, multi-line HEADER skipping, and direct COPY TO
on partitioned tables. Smaller items: GRANTED BY, CHECK constraint ENFORCED toggles, oid8,
jsonpath string methods, temporal random(), base64url and base32hex, and several utility
functions and casts. SQL and COPY covers them.
Maintenance extensions
Four contrib modules did not change in 19 but became more relevant to it: pg_visibility verifies
the visibility-map bits that table scans can now set, pgrowlocks shows per-row locks where the new
pg_stat_lock shows only per-type contention, pg_surgery is the last resort when corruption makes
a table unvacuumable - the worst possible position under the new freeze scoring - and pg_prewarm
warms caches around restart and benchmarking.
Maintenance extensions covers the
functions and the cautions.
Performance
The smaller executor and planner changes, which do not each rate a page:
NOT INconverts to anANTI JOINwhen NULLs cannot occur; moreLEFT JOINs convert too, and Memoize applies to anti joins with unique inner sides.- Foreign-key constraint checks are faster, which shows up on write-heavy workloads against referenced tables.
- Sorting uses radix sort where applicable; hash join handles NULL join keys better; some
aggregate work can be pushed below joins;
TIDrange scans can be parallelised. COPY FROMtext/CSV parsing andhex_encode/hex_decodeuse SIMD CPU instructions; internal row deformation and UTF-8 case folding are faster; hash index bulk-deletion andGINvacuuming use streaming reads.NOTIFYnow wakes only backends listening on the notified channel - previously most backends woke on any notification.- Table scans can set visibility-map all-visible bits, previously a
VACUUM/COPY FREEZEjob. timing_clock_sourcecontrols the timing source;EXPLAIN (ANALYZE, TIMING)andpg_test_timingbenefit.
What is not in 19
The SQL/PGQ property-graph syntax (GRAPH_TABLE) does not appear in the release notes and its
documentation pages return 404 - it is not shipping in 19 despite occasional write-ups listing it.
Also absent from the release: io_uring remains unavailable as an io_method in the documented
options.
Upgrade breakers
These are the incompatibilities listed in the release notes’ migration section - the changes that make an upgrade fail or behave differently rather than merely add features. The last column is what to check on your current cluster before you run pg_upgrade.
| Change in 19 | Effect | Check before upgrading |
|---|---|---|
| JIT disabled by default | Large analytical queries lose JIT compilation unless jit = on is set |
Whether your workload relies on JIT; plan to enable it explicitly |
max_locks_per_transaction default 64 to 128 |
Lock entry size allocation changed, so values must be doubled to match previous capacity | Any tuned max_locks_per_transaction or max_pred_locks_per_transaction needs doubling, not carrying over |
default_toast_compression now lz4 |
New TOASTed values compress differently; old pglz values still read |
Nothing breaks; mixed compression is normal. Verify lz4 is acceptable where CPU is constrained |
wal_compression = on picks an available method rather than pglz |
Previously on always meant pglz |
WAL volume and CPU may shift on upgrade; check if you pinned a method assuming pglz |
| RADIUS authentication removed | radius in pg_hba.conf no longer exists; PostgreSQL only ever supported RADIUS over UDP, which the project calls unfixably insecure |
Any pg_hba.conf line with radius; migrate to LDAP or certificate auth |
standard_conforming_strings forced on |
The setting can no longer be turned off server-side; escape_string_warning is removed |
Dumps made by pre-19 pg_dump with standard_conforming_strings = off will not load - re-dump with a 19 client or with the setting on |
| Warning after successful MD5 password authentication | md5_password_warnings (default on) logs a warning per login |
Rotate MD5 passwords to SCRAM; MD5 was deprecated in 18 |
password_expiration_warning_threshold |
New GUC warns about expiring passwords; default seven days | Expect new warnings in logs for roles near VALID UNTIL |
| CR/LF disallowed in database, role and tablespace names | pg_upgrade refuses clusters that use them | Check names for carriage returns or line feeds |
inet/cidr opclass change in btree_gist |
Default opclasses move to GiST; the old btree_gist opclasses were broken (could exclude rows that should be returned) | pg_upgrade refuses clusters with btree_gist inet/cidr indexes - drop and rebuild them on a fixed opclass |
json_array() with no rows returns [] |
Previously returned NULL |
Application code testing for NULL from json_array() |
COPY FROM ... WHERE rejects system columns |
Their values were never well-defined | COPY scripts filtering on xmin, ctid and similar |
postgres_fdw propagates READ ONLY/DEFERRABLE |
READ ONLY transactions can no longer write through foreign tables |
Anything deliberately writing via postgres_fdw inside read-only transactions |
log_lock_waits enabled by default |
Lock waits over deadlock_timeout are now logged out of the box |
Log volume on contended systems |
pg_stat_subscription_stats.sync_error_count renamed |
Now sync_table_error_count, because sequence errors are tracked separately in sync_seq_error_count |
Monitoring queries and dashboards referencing the old column name |
Wait event type BUFFERPIN renamed to BUFFER |
Monitoring queries grouping by wait event type | Dashboards and alerting on BUFFERPIN |
MULE_INTERNAL encoding removed |
Databases using it must be dumped and restored under a different encoding | SHOW server_encoding and per-database encodings |
\crosstabview honours \pset null |
psql output change for null cells | Scripts parsing crosstabview output |
Index AM handlers use static IndexAmRoutines; get_relation_info_hook removed |
Extension-level breakage: index access methods and planner hooks must be updated | Any C extensions or index AMs you carry; check they have 19 builds |
Several of these are silent behaviour changes rather than hard failures: log_lock_waits on by
default adds log lines, lz4 changes CPU characteristics of TOAST writes, and the NOT IN to anti
join transformation can change plans. The hard blockers are the ones pg_upgrade refuses on:
btree_gist inet/cidr indexes, CR/LF in names, and MULE_INTERNAL databases.
Where to next
Each sub-page goes deeper on one area, including the “what changed” tables against PostgreSQL 18:
- REPACK - the new core command and its
CONCURRENTLYmode versus the pg_repack extension - Replication and WAIT - read-your-writes on standbys, sequence replication,
EXCEPT,retain_dead_tuples - Autovacuum - parallel index vacuuming and the new scoring system
- Asynchronous I/O - worker autoscaling, read-ahead,
EXPLAIN (ANALYZE, IO) - Monitoring -
pg_stat_lock,pg_stat_recovery, wait events, logging changes - Plan advice -
pg_plan_adviceandpg_stash_advice - SQL and COPY - the smaller SQL additions and
NOT INto anti join - Maintenance extensions -
pg_visibility,pg_surgery,pg_prewarm,pgrowlocks: unchanged in 19, but the inspection tools for what it changed
What this page is not for
It is a map, not the territory: it does not replace the per-feature pages linked above, and it is not upgrade instructions. For the mechanics of an actual upgrade - pg_upgrade versus dump/restore versus logical replication - the release notes’ migration section and the installation page are the references. Everything here is beta documentation and can still change before GA.
Because 19 is pre-release, expect this area to be revised at each beta and again at GA.
PostgreSQL release support
5 supported| Release | Latest | Released | End of life | Status |
|---|---|---|---|---|
18 | 18.6 | Sept 2025 | Nov 2030 | Supported |
17 | 17.11 | Sept 2024 | Nov 2029 | Supported |
16 | 16.15 | Sept 2023 | Nov 2028 | Supported |
15 | 15.19 | Oct 2022 | Nov 2027 | Supported |
14 | 14.24 | Sept 2021 | Nov 2026 | Supported |
13 | 13.23 | Sept 2020 | Nov 2025 | End of life |
12 | 12.22 | Oct 2019 | Nov 2024 | End of life |
11 | 11.22 | Oct 2018 | Nov 2023 | End of life |
Data as of 2026-10-01 from endoflife.date. Refresh with node scripts/fetch-versions.mjs.
What to watch
Specific things that would change what this page concludes.
- The PostgreSQL 19 GA date and whether the beta feature set ships unchanged - beta features can still shift
- Whether REPACK CONCURRENTLY's replication-slot requirement (wal_level = logical) is relaxed before GA
Where PostgreSQL sits
How this component relates to the rest of the stack. Items without a link are not covered here yet.