IntermediateReferenceDatabases

PostgreSQL 19 - what changes and what breaks

PostgreSQL 19 release status, an overview of every feature area - REPACK, WAIT and sequence replication, parallel and scored autovacuum, plan advice, I/O autoscaling, the monitoring additions and the new SQL surface - and the full list of migration breakers to check before upgrading.

Applies to
PostgreSQL19PostgreSQL 19 was still in beta at the last review - Beta 4 (24 September 2026) is the latest pre-release and no release candidate had shipped. Everything on this page is written from the beta documentation and can still shift before GA.
emergingUpdated Reviewed
intermediateAssumes working familiarity; skips the fundamentals.

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 IN converts to an ANTI JOIN when NULLs cannot occur; more LEFT 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; TID range scans can be parallelised.
  • COPY FROM text/CSV parsing and hex_encode/hex_decode use SIMD CPU instructions; internal row deformation and UTF-8 case folding are faster; hash index bulk-deletion and GIN vacuuming use streaming reads.
  • NOTIFY now 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 FREEZE job.
  • timing_clock_source controls the timing source; EXPLAIN (ANALYZE, TIMING) and pg_test_timing benefit.

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 CONCURRENTLY mode 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_advice and pg_stash_advice
  • SQL and COPY - the smaller SQL additions and NOT IN to 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
ReleaseLatestReleasedEnd of lifeStatus
1818.6Sept 2025Nov 2030Supported
1717.11Sept 2024Nov 2029Supported
1616.15Sept 2023Nov 2028Supported
1515.19Oct 2022Nov 2027Supported
1414.24Sept 2021Nov 2026Supported
1313.23Sept 2020Nov 2025End of life
1212.22Oct 2019Nov 2024End of life
1111.22Oct 2018Nov 2023End 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.

Depends on

Must be present for this to run

Deployed with

Commonly run alongside

Alternatives

Solves a similar problem differently