Ricordo di aver sollevato un punto quasi identico quando PG9 era in stato alfa. Ecco la risposta di Tom Lane (sviluppatore principale PG di alto profilo):
http://archives.postgresql.org/pgsql-general/2010-01/msg00221.php
In breve:non si risolve.
Per non dire che sono d'accordo con il tuo suggerimento che il comportamento attuale è un bug. Guardalo dall'angolo opposto:è il comportamento di NOT DEFERRABLE
non è corretto.
Infatti la violazione del vincolo in questo UPDATE non dovrebbe mai verificarsi in ogni caso, poiché al termine dell'UPDATE il vincolo è soddisfatto. Lo stato alla fine del comando è ciò che conta. Gli stati intermedi durante l'esecuzione di una singola istruzione non dovrebbero essere esposti all'utente.
Sembra che PostgreSQL implementi il vincolo non differibile controllando i duplicati dopo ogni riga aggiornata e fallendo immediatamente al primo duplicato, il che è essenzialmente difettoso. Ma questo è un problema noto, probabilmente vecchio quanto PostgreSQL. Al giorno d'oggi la soluzione alternativa è proprio usare un vincolo DEFERRABLE. E c'è un po' di ironia nel fatto che lo consideri carente perché non riesce a fallire, mentre in qualche modo dovrebbe essere la soluzione al fallimento in primo luogo!
Riepilogo dello status quo da PostgreSQL 9.1
-
NOT DEFERRABLE
UNIQUE
oPRIMARY KEY
i vincoli vengono controllati dopo ogni riga . -
DEFERRABLE
vincoli impostati suIMMEDIATE
(INITIALLY IMMEDIATE
o tramiteSET CONSTRAINTS
) vengono controllati dopo ogni affermazione . -
DEFERRABLE
vincoli impostati suDEFERRED
(INITIALLY DEFERRED
o tramiteSET CONSTRAINTS
) vengono controllati dopo ogni transazione .
Nota il trattamento speciale di UNIQUE
/ PRIMARY KEY
vincoli. Citando la pagina di manuale per CREATE TABLE
:
Un vincolo non differibile verrà verificato immediatamente dopo ogni comando .
Mentre viene indicato più in basso nella Compatibilità sezione in Non-deferred uniqueness constraints
:
Quando un UNIQUE
o PRIMARY KEY
il vincolo non è differibile, PostgreSQL verifica immediatamente l'unicità ogni volta che una riga viene inserita o modificata. Lo standard SQL dice che l'unicità dovrebbe essere forzata solo alla fine dell'istruzione; questo fa la differenza quando, ad esempio, un singolo comando aggiorna più valori chiave. Per ottenere un comportamento conforme allo standard, dichiara il vincolo come DEFERRABLE
ma non differito (cioè, INITIALLY IMMEDIATE
). Tieni presente che questo può essere significativamente più lento del controllo immediato dell'unicità.
Enfasi in grassetto la mia.
Se hai bisogno di qualsiasi FOREIGN KEY
vincoli per fare riferimento alle colonne, DEFERRABLE
non è un'opzione perché (per documentazione):
Le colonne di riferimento devono essere le colonne di un vincolo di chiave primaria o univoca non differibile nella tabella di riferimento.