PostgreSQL 18 🐟 Nuove funzionalita'

La versione PostgreSQL 18 introduce nuove funzionalita' all'RDBMS Open Source piu' avanzato ed oggi [NdA 2025-04-01] e' il giorno giusto per parlarne: ecco quindi pubblicato questo documento!

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, utile ad esempio quando si utilizzano colonne generate:

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

Con la versione 18 ora 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

Ovviamente per le DELETE i NEW sono NULL e per le INSERT gli OLD sono NULL, ma c'e' anche il caso di INSERT ... ON CONFLICT in cui possono essere restituiti i valori precedenti!

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' viene ora definita 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 ma sono ricalcolati ogni volta come avviene, per esempio, nelle viste.
Ora definendo una generated column la modalita' VIRTUAL e' quella di default, 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 contengono valori a NULL quindi danno comunque un vantaggio in termini di spazio.

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.

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, ...

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.

I piu' attenti si saranno accorti che si potrebbe trattare di casi in cui le query o il disegno fisico non sono ottimali [NdA ovvero sono sbagliati o realizzati da incompetenti]... puo' anche essere, diciamo la versione 18 potra' avere buone prestazioni anche in questi casi!

Logging connections

Gli aspetti di sicurezza sono sempre tenuti in grande conto da PostgreSQL. E' noto spesso che connessioni alla base dati vadano 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'
nel file di configurazione. 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 come in precedenza:
 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!

Ecco il contenuto completo della matrice delle nuove funzionalita' di PostgreSQL 18: Hash Right Semi Join Parallel Execution Enhancements Better Logical Replication Conflict Resolution Planner: Hash Right Semi Join support Planner: materializing an internal row set for parallel nested loop join Planner support functions for generate_series EXPLAIN (analyze): statistics for Parallel Bitmap Heap Scan node workers Functions min and max for composite types Parameter names for regexp* functions Debug mode in pgbench pg_get_backend_memory_contexts: column path instead of parent, new column type Function pg_get_acl pg_upgrade: pg_dump optimization Predefined role pg_signal_autovacuum_worker Temporal PRIMARY KEY and UNIQUE constraints, two phase commit option in subscriptions, PL/Tcl support for Tcl 9, max() and min() for bytea, ... virtual generated columns support for OLD/NEW added to RETURNING clause in INSERT, UPDATE, MERGE DISTINCT keys can be reordered to match input path's pathkeys redundant GROUP BY columns can now be detected using unique indexes Self-Join Elimination (SJE) Partitions pruned by initial pruning are no longer locked log_connections = 'authorization','receipt','authentication'; pg_upgrade swap mode April the 1st is too early!

Il riferimento finale e' la documentazione ufficiale [NdE per quando ci sara'... al momento documentazione e' in sviluppo] e l'ottimo prospetto riassuntivo di pgPedia [NdA per i piu' audaci ci sono i Commitfest: 2024-07, 2024-09, 2024-11, 2025-01, 2025-03, 2025-07 ed analizzare i committed].

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 ma utile documento con la storia delle versioni di tutti i software che ritengo piu' significativi, ed ovviamente anche di 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.


Titolo: PostgreSQL 18 - Nuove funzionalita'
Livello: Avanzato (3/5)
Data: 1 Aprile 2025
Versione: 1.0.1 - 1 Aprile 2025 🐟
Autore: mail [AT] meo.bogliolo.name

pg_catalog | pg_get_process_memory_contexts | SETOF record | pid integer, summary boolean, retries double precision, OUT name text, OUT ident text, OUT type text, OUT path integer[], OUT level integer, OUT total_bytes bigint, OUT 17=# EXPLAIN (costs off) SELECT * FROM flights WHERE flight_id = 1 OR flight_id = 2 OR flight_id = 3; The query could be rewritten more efficiently, so that a single index scan is enough: 17=# EXPLAIN (costs off) SELECT * FROM flights WHERE flight_id = ANY (ARRAY[1,2,3]); -->