Alcune risposte suggeriscono di utilizzare il pattern: controlla se il ruolo non esiste e in caso contrario emetti il CREATE ROLEcomando. Questo ha uno svantaggio: le condizioni di gara. Se qualcun altro crea un nuovo ruolo tra il controllo e l'emissione del CREATE ROLEcomando, CREATE ROLEovviamente fallisce con un errore fatale.
Per risolvere il problema di cui sopra, più altre risposte già menzionate nell'uso PL/pgSQL, emettendo CREATE ROLEincondizionatamente e quindi intercettando le eccezioni da quella chiamata. C'è solo un problema con queste soluzioni. Eliminano silenziosamente qualsiasi errore, inclusi quelli che non sono generati dal fatto che il ruolo esiste già. CREATE ROLEpuò generare anche altri errori e la simulazione IF NOT EXISTSdovrebbe tacitare solo l'errore quando il ruolo esiste già.
CREATE ROLEgenera un duplicate_objecterrore quando il ruolo esiste già. E il gestore delle eccezioni dovrebbe rilevare solo questo errore. Come menzionato in altre risposte, è una buona idea convertire l'errore fatale in semplice avviso. Altri IF NOT EXISTScomandi PostgreSQL si aggiungono , skippingal loro messaggio, quindi per coerenza lo aggiungo anche qui.
Ecco il codice SQL completo per la simulazione CREATE ROLE IF NOT EXISTScon l'eccezione corretta e la propagazione sqlstate:
DO $$
BEGIN
CREATE ROLE test;
EXCEPTION WHEN duplicate_object THEN RAISE NOTICE '%, skipping', SQLERRM USING ERRCODE = SQLSTATE;
END
$$;
Uscita di prova (chiamata due volte tramite DO e poi direttamente):
$ sudo -u postgres psql
psql (9.6.12)
Type "help" for help.
postgres=# \set ON_ERROR_STOP on
postgres=# \set VERBOSITY verbose
postgres=#
postgres=# DO $$
postgres$# BEGIN
postgres$# CREATE ROLE test;
postgres$# EXCEPTION WHEN duplicate_object THEN RAISE NOTICE '%, skipping', SQLERRM USING ERRCODE = SQLSTATE;
postgres$# END
postgres$# $$;
DO
postgres=#
postgres=# DO $$
postgres$# BEGIN
postgres$# CREATE ROLE test;
postgres$# EXCEPTION WHEN duplicate_object THEN RAISE NOTICE '%, skipping', SQLERRM USING ERRCODE = SQLSTATE;
postgres$# END
postgres$# $$;
NOTICE: 42710: role "test" already exists, skipping
LOCATION: exec_stmt_raise, pl_exec.c:3165
DO
postgres=#
postgres=# CREATE ROLE test;
ERROR: 42710: role "test" already exists
LOCATION: CreateRole, user.c:337