PostgreSQL 19 Beta 1: due novità che meritano attenzione
Il 4 giugno 2026 il PostgreSQL Global Development Group ha rilasciato la prima beta di PostgreSQL 19, con disponibilità generale attesa tra settembre e ottobre 2026. Tra le decine di miglioramenti elencati nelle release notes, due si distinguono per l’impatto pratico su chi gestisce database in produzione: il supporto nativo alle SQL Property Graph Queries (SQL/PGQ) e il nuovo comando REPACK, che introduce finalmente una modalità CONCURRENTLY per riorganizzare le tabelle senza bloccare le scritture. Vale la pena approfondire entrambe, perché rispondono a due esigenze molto concrete: interrogare relazioni complesse senza migrare verso un database a grafo dedicato, e liberare spazio su disco senza fermare l’applicazione.
SQL/PGQ: query a grafo sulle tue tabelle relazionali esistenti
SQL/PGQ è la Parte 16 dello standard ISO/IEC 9075 (SQL:2023) e definisce un modo standard per esprimere query di pattern matching su grafi usando sintassi SQL. La scelta implementativa di PostgreSQL è particolarmente interessante: un property graph non è una nuova struttura di storage, ma una vista in sola lettura sopra tabelle relazionali già esistenti. I dati restano dove sono sempre stati — in tabelle normali, con vincoli di chiave primaria ed esterna — e vengono semplicemente “esposti” come grafo per le query che lo richiedono. Query relazionali e query a grafo condividono lo stesso planner e lo stesso motore di esecuzione, e possono anche essere combinate nella stessa istruzione.
Per capire la sintassi conviene partire da uno schema minimo di e-commerce:
CREATE TABLE products (
product_no integer PRIMARY KEY,
name varchar,
price numeric
);
CREATE TABLE customers (
customer_id integer PRIMARY KEY,
name varchar,
address varchar
);
CREATE TABLE orders (
order_id integer PRIMARY KEY,
ordered_when date
);
CREATE TABLE order_items (
order_items_id integer PRIMARY KEY,
order_id integer REFERENCES orders (order_id),
product_no integer REFERENCES products (product_no),
quantity integer
);
CREATE TABLE customer_orders (
customer_orders_id integer PRIMARY KEY,
customer_id integer REFERENCES customers (customer_id),
order_id integer REFERENCES orders (order_id)
);
Le prime tre tabelle diventano i vertici del grafo, le ultime due — grazie alle chiavi esterne che collegano coppie di vertici — diventano gli archi. La definizione del grafo è dichiarativa:
CREATE PROPERTY GRAPH myshop
VERTEX TABLES (
products LABEL product,
customers LABEL customer,
orders LABEL "order"
)
EDGE TABLES (
order_items SOURCE orders DESTINATION products LABEL contains,
customer_orders SOURCE customers DESTINATION orders LABEL has_placed
);
Da qui in poi si può interrogare il grafo con la clausola GRAPH_TABLE e la sintassi di pattern matching, invece delle classiche JOIN:
SELECT customer_name
FROM GRAPH_TABLE (
myshop
MATCH (c IS customer)-[IS has_placed]->(o IS "order" WHERE o.ordered_when = current_date)
COLUMNS (c.name AS customer_name)
);
che corrisponde, dal punto di vista relazionale, a:
SELECT customers.name
FROM customers
JOIN customer_orders USING (customer_id)
JOIN orders USING (order_id)
WHERE orders.ordered_when = current_date;
La differenza diventa evidente man mano che i pattern si complicano: catene di relazioni a più salti, percorsi variabili, ricerche “chi è collegato a chi entro N passaggi” sono espressioni naturali in sintassi a grafo, ma diventano rapidamente catene di self-join illeggibili in SQL relazionale puro. Per chi lavora su organigrammi, grafi di dipendenze tra servizi, reti di frodi o raccomandazioni, SQL/PGQ evita di dover affiancare un database a grafo dedicato (Neo4j, Amazon Neptune) solo per questo tipo di interrogazioni, con tutto il costo operativo che una piattaforma aggiuntiva comporta.
È bene ricordare che, essendo una vista sopra le tabelle esistenti, un grafo così definito eredita automaticamente permessi, vincoli e integrità referenziale già presenti: non introduce una nuova copia dei dati da tenere sincronizzata.
REPACK CONCURRENTLY: addio alle finestre di manutenzione per VACUUM FULL
Chi amministra PostgreSQL conosce bene il dilemma tra VACUUM normale, che non recupera sempre tutto lo spazio occupato da tuple morte, e VACUUM FULL (o CLUSTER), che lo recupera per intero ma richiede un ACCESS EXCLUSIVE lock per l’intera durata dell’operazione — inaccettabile su una tabella calda in produzione. PostgreSQL 19 risolve il problema unificando le due funzionalità in un unico comando, REPACK, e aggiungendo un’opzione CONCURRENTLY che cambia le regole del gioco.
-- Riorganizza una tabella riscrivendola su nuovo file, senza clustering
REPACK employees;
-- Riorganizza mantenendo l'app pienamente operativa in lettura/scrittura
REPACK (CONCURRENTLY) employees;
-- Repack con clustering fisico secondo un indice, più ANALYZE finale
REPACK (ANALYZE, VERBOSE) cases (district, case_nr);
Il meccanismo interno spiega perché questa opzione è sicura da usare su tabelle attive: REPACK copia il contenuto della tabella (ignorando le tuple morte) in un nuovo file, ordinato secondo l’indice specificato se presente, e crea nuovi file anche per gli indici collegati. Con CONCURRENTLY, l’ACCESS EXCLUSIVE lock viene acquisito solo per lo scambio finale dei file: le modifiche avvenute durante la copia vengono catturate tramite logical decoding e riapplicate prima dello swap, che quindi risulta tipicamente molto breve. La tabella resta leggibile e scrivibile per la quasi totalità dell’operazione.
Ci sono comunque limiti operativi da conoscere prima di affidarsi a questa modalità in produzione: CONCURRENTLY non è disponibile su tabelle UNLOGGED, su tabelle partizionate, su tabelle prive di chiave primaria e di un’identità di replica basata su indice, su cataloghi di sistema o tabelle TOAST, né dentro un blocco di transazione. Serve inoltre che max_repack_replication_slots consenta la creazione di uno slot di replica aggiuntivo, dato che il meccanismo si appoggia alla logical decoding. Va anche segnalato che REPACK CONCURRENTLY non è MVCC-safe nello stesso senso di altre operazioni online, quindi vale la pena leggere con attenzione la sezione sulle caveat di MVCC nella documentazione prima di pianificarne l’adozione su carichi critici.
Il progresso dell’operazione è osservabile in tempo reale tramite la vista pg_stat_progress_repack, utile per stimare i tempi su tabelle di grandi dimensioni prima di schedulare l’operazione su ambienti con SLA stringenti.
Altre novità rilevanti per chi amministra database in produzione
Oltre alle due funzionalità principali, la beta introduce una serie di miglioramenti che meritano di essere tenuti d’occhio in fase di test: l’autovacuum può ora usare worker paralleli (autovacuum_max_parallel_workers), le inserzioni con controlli di chiave esterna sono fino a due volte più veloci, e il nuovo comando WAIT FOR LSN permette di implementare pattern “read-your-writes” verso le repliche senza più bisogno di sleep applicativi o di forzare le letture sul primario. Da segnalare anche un cambiamento di default che avrà impatto diretto sui workload analitici: la compilazione JIT (jit) è ora disattivata di default, il che può alterare sensibilmente i piani e i tempi di query che finora beneficiavano (o soffrivano) silenziosamente della compilazione just-in-time.
Conclusione
PostgreSQL 19 conferma la strategia degli ultimi anni: estendere le capacità del database relazionale invece di costringere i team a moltiplicare le piattaforme specializzate. SQL/PGQ evita un database a grafo dedicato per molti casi d’uso comuni, mentre REPACK CONCURRENTLY chiude una lacuna operativa che gli amministratori PostgreSQL si portavano dietro da anni. Trattandosi ancora di una beta, entrambe le funzionalità vanno testate su ambienti non di produzione prima della GA prevista per l’autunno 2026, ma la direzione presa merita già di essere seguita da vicino da chi pianifica le prossime migrazioni.
Fonte: PostgreSQL Global Development Group — PostgreSQL 19 Beta 1 Released!