PostgreSQL 19 Beta 1 landed on June 4, 2026, and it’s packed with features that directly change how you write queries, maintain tables, and manage replicas. The release is expected to reach general availability around September or October 2026, but the beta already gives a clear picture of what’s coming. Let’s walk through the features that matter most to developers working with PostgreSQL day to day.
GROUP BY ALL: Stop Listing Columns Manually
One of those small quality-of-life improvements that saves real time: GROUP BY ALL automatically groups by every non-aggregate, non-window-function column in your SELECT list. Instead of spelling out each column twice — once in SELECT, once in GROUP BY — you write it once and let PostgreSQL figure out the grouping.
-- Before: repeating every grouping column
SELECT country, city, category, count(*), avg(amount)
FROM orders
GROUP BY country, city, category;
-- PostgreSQL 19: GROUP BY ALL handles it
SELECT country, city, category, count(*), avg(amount)
FROM orders
GROUP BY ALL;
This is particularly useful in analytical queries where you frequently add or remove columns from the SELECT list. With GROUP BY ALL, you adjust the output columns and the grouping adapts automatically — no risk of a mismatched GROUP BY clause causing a runtime error. If a column isn’t an aggregate or window function, it gets included in the grouping.
INSERT … ON CONFLICT DO SELECT: Handle Conflicts Without Discarding Data
UPSERT via INSERT ... ON CONFLICT DO UPDATE has been PostgreSQL’s go-to for handling duplicate-key situations since version 9.5. PostgreSQL 19 adds a third option: INSERT ... ON CONFLICT DO SELECT ... RETURNING. Instead of silently discarding the conflicting row or overwriting it, you can return the row that already exists — and optionally lock it with FOR UPDATE or FOR SHARE.
INSERT INTO active_sessions (user_id, token, expires_at)
VALUES (42, 'new_token', now() + interval '1 hour')
ON CONFLICT (user_id) DO SELECT
RETURNING *;
This pattern is useful when you need to detect whether a row already exists and then act on that information in application code — all in a single round trip. It avoids the race condition inherent in a separate SELECT-then-INSERT approach and avoids the overhead of DO UPDATE when you genuinely don’t want to modify the existing row.
REPACK CONCURRENTLY: Rebuild Tables Without Locking Out Writes
Anyone who has run VACUUM FULL or CLUSTER on a large production table knows the pain: both commands require an ACCESS EXCLUSIVE lock, blocking all reads and writes for the duration. PostgreSQL 19 introduces REPACK, which unifies the purpose of both commands into one, and adds a CONCURRENTLY option that rebuilds the table without taking an access-exclusive lock.
-- The new way: rebuild without blocking writes
REPACK TABLE orders CONCURRENTLY;
-- The old way still works, but locks the table
REPACK TABLE orders;
REPACK CONCURRENTLY uses replication slots internally to track changes during the rebuild, following a pattern similar to REINDEX CONCURRENTLY (available since PostgreSQL 12). The new max_repack_replication_slots configuration parameter controls the slots used by this operation. For production systems dealing with table bloat on high-traffic tables, this eliminates one of the most disruptive maintenance operations.
WAIT FOR LSN: Read-Your-Writes on Replicas
One of the persistent challenges with read replicas is the replication lag: you write to the primary, then immediately read from a replica, and the data isn’t there yet. PostgreSQL 19 introduces WAIT FOR LSN, which lets a session on a replica block until changes up to a specific log sequence number have been replayed.
-- On the primary: get the LSN after your write
INSERT INTO events (user_id, action) VALUES (42, 'purchase');
-- On the replica: wait until that LSN is replayed
WAIT FOR LSN '0/1A2B3C4' REPLAYED;
SELECT * FROM events WHERE user_id = 42;
This gives you “read-your-writes” consistency on replicas without routing all reads through the primary. The WAIT FOR command supports three targets — WRITTEN, FLUSHED, and REPLAYED — giving you fine-grained control over how much certainty you need before proceeding. For applications with high read volumes that tolerate slightly stale data in most cases but need freshness after writes, this is a cleaner alternative to synchronous replication or primary-only reads.
Parallel Autovacuum and Smarter Vacuum Scheduling
Vacuuming is one of PostgreSQL’s most critical maintenance operations, and it’s also one of the most common bottlenecks on write-heavy workloads. PostgreSQL 19 lets autovacuum use parallel workers for the first time, configured via the autovacuum_max_parallel_workers setting (default: 2).
Alongside parallel vacuum, PostgreSQL 19 introduces a scoring system that prioritizes which tables to vacuum first. Five new parameters control the scoring weights: autovacuum_freeze_score_weight, autovacuum_multixact_freeze_score_weight, autovacuum_vacuum_score_weight, vacuum_insert_score_weight, and autovacuum_analyze_score_weight. The default weights treat all factors equally, but you can tune them based on your workload — for example, giving higher weight to vacuum threshold violations on tables with heavy update/delete activity.
Additionally, regular query table scans can now mark pages as all-visible in the visibility map, a task previously reserved for VACUUM and COPY ... FREEZE. This means the database progressively reduces future vacuum work during normal operation, not just during dedicated maintenance windows.
String Processing in JSONPath
PostgreSQL’s jsonpath support gets a substantial expansion with seven new string functions: lower(), upper(), initcap(), replace(), split_part(), and the trim() family (ltrim(), rtrim(), btrim()). These work the same way as their SQL counterparts but operate within jsonpath expressions.
SELECT jsonb_path_query(
'{"users": [{"name": "JOHN DOE"}, {"name": "jane smith"}]}',
'$.users[*].name ? (@.lower() like "john%")'
);
These functions are immutable, matching the behavior of their standard SQL equivalents. They make jsonpath queries significantly more practical for searching and transforming JSON data without having to extract values into SQL first, process them, and put them back.
Other Notable Changes
A few more features worth knowing about:
Logical replication without restart. You can now enable logical replication on demand even when wal_level is set to replica, and the system automatically promotes to logical decoding when needed. This eliminates the need to commit upfront to a higher WAL level or restart the server.
Online data checksums. Data checksums can now be enabled or disabled without restarting the cluster or reinitializing it — previously this required taking the database offline and running pg_checksums.
Faster inserts with foreign keys. Insert performance with foreign key constraint checks present has been significantly improved, which directly benefits workloads with heavily normalized schemas.
LZ4 as default compression. The default_toast_compression setting now defaults to lz4 instead of pglz, providing better compression and decompression performance out of the box.
SNI support for TLS. A single PostgreSQL server can now present different TLS certificates based on the hostname the client connects to, configured via a new pg_hosts.conf file.
Deprecated and removed. JIT compilation is now disabled by default. RADIUS authentication has been removed entirely. MD5 password authentication issues a warning after successful login, continuing its deprecation path from PostgreSQL 18.
What to Expect Next
PostgreSQL 19 is currently in beta — the feature set is largely frozen, but details can still change. The project expects additional beta releases followed by release candidates before the final GA around September or October 2026. Now is the time to test against your workloads and report any issues through the PostgreSQL bug tracker.
The release notes, which cover every change in detail, are available at the PostgreSQL 19 documentation site. The beta binaries can be downloaded from the beta testing page.