PostgreSQL 18 - Nuove funzionalita'

PostgreSQL version 18 La versione PostgreSQL 18 introduce nuove funzionalita' all'database relazionale Open Source piu' avanzato ed oggi [NdA 2025-09-25 e non il primo di Aprile 🐟] e' il giorno giusto per parlarne: ecco quindi pubblicata questa paginetta!

In generale la versione 18 e' un'evoluzione della versione precedente con nuove funzionalita' (eg. virtual generated columns) ma anche con migliori prestazioni ed un costante miglioramento degli aspetti di gestione delle basi dati.

In questo documento sono riportati in dettaglio i principali nuovi elementi introdotti nella versione 18 riportando esempi pratici di utilizzo:

Ma le novita' non sono solo queste... continuate a leggere!

Supporto per OLD/NEW in RETURNING

Le istruzioni di INSERT, UPDATE, DELETE e MERGE supportano da tempo la clausola RETURNING che riporta i valori inseriti:

postgres=# create table t (a bigint generated always as identity, b text); CREATE TABLE postgres=# insert into t(b) values ('Ciao') returning *; a | b ---+------ 1 | Ciao

Ci si potrebbe chiedere ma a cosa servono all'applicazione o all'utente i valori inseriti, visto che li ha appena inseriti? In realta' e' utile quando si utilizzano colonne generate.

Dalla versione 18 e' possibile accedere anche ai valori precedenti con una semplice sintassi:

postgres=# update t set b = 'Hello' returning old.b as before, new.b as after; before | after --------+------- Ciao | Hello

Molto utile con le UPDATE! Ovviamente nel caso di una DELETE i valori NEW sono NULL e per una INSERT i valori OLD sono NULL. La possibilita' di disporre dei valori precedenti e' utile nel caso di INSERT... ON CONFLICT in cui possono essere restituiti i valori prima della modifica!

Virtual generated columns

Le generated columns, ovvero le colonne i cui valori vengono automaticamente generati da un espressione, sono disponibili in PostgreSQL dalla versione 12, ed i valori calcolati sono memorizzati nella heap come per tutte le altre colonne. Questa modalita' e' chiamata STORED generated columns.

Con la versione 18 sono disponibili le VIRTUAL generated columns che svolgono le stesso compito ma i cui valori non vengono memorizzati e sono ricalcolati ogni volta come avviene nelle viste.

alter table emp add column total_sal numeric generated always as ( sal+coalesce(comm) ) VIRTUAL;
Dalla versione 18 la modalita' VIRTUAL e' quella di default per creare una generated column, per utilizzare la vecchia modalita' va specificata la clausola STORED.

Nota: per una serie di ragioni pratiche le virtual generated columns sono implementate come le stored generated columns e quindi memorizzate fisicamente nella heap, ma i placeholder contengono valori a NULL e quindi danno comunque un vantaggio in termini di occupazione dello spazio.

NOT NULL constraints NOT VALID

La versione 18 introduce la possibilita' di aggiungere un constraint NOT NULL come non valido:

-- Richiede un lock "pesante" ma veloce perche' non deve validare il constraint ALTER TABLE table_name ADD CONSTRAINT constraint_name NOT NULL (column_name) NOT VALID; -- Richiede un lock "solo" ShareUpdateExclusiveLock (permette DML) ALTER TABLE table_name VALIDATE CONSTRAINT constraint_name;

Sembra una banalita'... ma una ALTER TABLE richiede un pesante lock ACCESS EXCLUSIVE e la verifica del constraint puo' richiedere molto tempo su una tabella di grandi dimensioni, bloccando cosi' tutti gli accessi.
Aggiungendo il constraint come non valido invece la verifica puo' essere eseguita in un momento successivo e con un piu' leggero lock SHARE UPDATE EXCLUSIVE. Inoltre il constraint appena definito, anche se dichiarato NOT VALID, viene comunque applicato sulle nuove righe inserite nella tabella ed e' quindi immediatamente efficace.

Planner

Il planner e' il componente che decide qual'e' l'algoritmo piu' efficiente per rispondere ad una query. Nella versione 18 vi sono alcune novita' che possono rendere piu' performanti alcune tipologie di query: Hash Right Semi Join, materializing sui parallel nested loop, ordinamento per la DISTINCT, eliminazione dei Self-Join, applicare lo skip scan sugli indici, ...

Addentrarsi nei dettagli tecnici non e' semplice ma ci proviamo lo stesso!
Con i Hash Right Semi Join vengono ottimizzate quelle query su tabelle che non hanno indici adatti su cui le versioni precedenti di Postgres spesso sceglievano di creare un indice hash sulla tabella "meno adatta". I self join vengono spesso introdotti artificialmente da ORM e la loro eliminazione, quando possibile, porta a significativi vantaggi prestazionali. Nel caso di dipendenze funzionali utilizzare un ordine differente per realizzare un DISTINCT rende piu' veloce l'elaborazione.
Lo skip scan sugli indici multicolumn invece riguarda la possibilita' di utilzzare un indice multicolonna anche nel caso in cui non tutte le colonne dell'indice siano presenti come chiavi di ricerca.

I piu' attenti si saranno accorti che si trattata di casi in cui le query o il disegno fisico non sono ottimali [NdA per dirlo in modo chiaro: sono casi in cui gli indici e le query sono sbagliati e/o realizzate in automatico e/o realizzati da incompetenti]; in effetti e' cosi'... diciamo la versione 18 ha buone prestazioni anche in questi casi che in precedenza avrebbero richiesto di correggere il disegno fisico o l'applicazione!

Temporal Constraint

L'idea dei constraint temporali e' semplice... si tratta di una chiave che fa riferimento ad un periodo di tempo. Dalla versione 18 e' possibile definire foreign constraint temporali basati sui datatype di range temporali di PostgreSQL.

Ecco un esempio di sintassi PG18 per definire il legame tra una persona ed un suo documento:

create table document ( id bigint generated BY DEAFULT AS IDENTITY, code text NOT NULL, authority text NOT NULL, validity tstzrange NOT NULL DEFAULT tstzrange(now(), 'infinity', '[)'), image bytea, note text, PRIMARY KEY (id, validity WITHOUT OVERLAPS) ); create table visitor ( id bigint generated ALWAYS AS IDENTITY, surname text not null, name text not null, birth_date timestamp, document_id bigint not null, document_valid_at tstzrange not null, note text, CONSTRAINT visitor_document foreign key (document_id, PERIOD document_valid_at) REFERENCES document (id, PERIOD validity) );

Nell'esempio abbiamo una temporal primary key sulla tabella che contiene i documenti ed una temporal foreign key sulla tabella del visitatore. Per l'implementazione della primary key PostgreSQL utilizza un indice GiST.

Logging connections

Gli aspetti di sicurezza sono sempre tenuti in grande conto da PostgreSQL. Spesso che connessioni alla base dati debbono essere registrate [NdA in Italia dal 2009 vanno registrate tutte le connessioni amministrative, come gia' descritto in questa paginetta]. PostgreSQL consente da sempre tale registrazione nel file di log con l'impostazione:
 log_connections = 'on'
Tuttavia con tale impostazione una connessione viene registrata tre volte nel file di log perche' tecnicamente vengono riportate le tre fasi della connessione. Con la versione 18 il parametro non e' piu' un booleano e consente di indicare quale fase registrare scegliendo tra esse o riportandole tutte per ottenere lo stesso logging logorroico [NdA perdonate... mi piaceva la paronomasia] delle precedenti versioni:
 log_connections = 'authorization','receipt','authentication'

Varie ed eventuali

Quelle riportate fino ad ora non sono le uniche variazioni importanti della versione 18 di PostgreSQL. Altre novita' interessanti sono:

Avro' dimenticato qualcosa? Certamente si!
Non ho detto che nell'EXPLAIN ora il parametro BUFFERS e' impostato per default e che e' stata introdotta l'utile funzione pg_get_acl()...

Ecco il contenuto della matrice delle nuove funzionalita' di PostgreSQL 18: UUIDv7, Parallelized CREATE INDEX for GIN indexes, Skip scan on multicolumn B-tree indexes, Return OLD and NEW values from modified rows, Temporal constraints, Virtual Generated Columns, Asynchronous I/O (AIO), Logical replication of stored generated columns, Logical replication write conflict logging, pg_upgrade --swap, Planner statistics preserved on major version upgrade, FIPS mode validation, OAuth Authentication / Authorization, SHA-2 encryption for password hashing, TLS v1.3 cipher suite allowlisting, CREATE FOREIGN TABLE... LIKE, postgres_fdw SCRAM authentication passthrough, casefold, LIKE comparisons for nondeterministic collations, pg_unicode_fast collation, psql pipeline queries, psql named prepared statements, pg_logicalinspect, pg_overexplain.

Il riferimento finale e' la documentazione ufficiale e l'ottimo prospetto riassuntivo di pgPedia.

PostgreSQL e' in costante evoluzione! Per il passato: le novita' della versione 17, le novita' della versione 16, le novita' della versione 15, le novita' della versione 14, evoluzione di PostgreSQL, ... Il tuo server puzza! e' il famigerato documento, ma spesso utile, con la storia delle versioni di tutti i software che ritengo piu' significativi, ed ovviamente riporta un ampia sezione anche per PostgreSQL.

Per il futuro... in realta' il futuro di PostgreSQL e' gia' adesso perche' gli sviluppi per la versione 18 sono gia' iniziati [NdA 2024-07] ed ad aprile 2025 si arrivera' al feature freeze in cui vengono fissate le nuove funzionalita' che verranno aggiunte nella versione. Arriveranno quindi le prime Beta e quindi una o piu' RC (Release Candidate) a seconda delle necessita'. Indicativamente a settembre 2025 la nuova versione PG18 sara' disponibile come produzione.

Breaking news

Oggi [NdA 2025-09-25] la versione PostgreSQL 18 e' stata rilasciata in produzione!

Non sono molte le variazioni rispetto alla versione iniziale 🐟 di questa pagina... a volte e' facile prevedere il futuro. In realta' non e' cosi' ed i dettagli contenuti in questa pagina sono molto piu' precisi, come avra' notato chi ha letto fino a qui!


Titolo: PostgreSQL 18 - Nuove funzionalita'
Livello: Avanzato (3/5)
Data: 25 Settembre 2025
Versione: 1.1.0 - 25 Settembre 2025
Autore: mail [AT] meo.bogliolo.name