Alcune risposte suggeriscono di utilizzare il pattern: controlla se il ruolo non esiste e in caso contrario emetti il CREATE ROLE
comando. Questo ha uno svantaggio: le condizioni di gara. Se qualcun altro crea un nuovo ruolo tra il controllo e l'emissione del CREATE ROLE
comando, CREATE ROLE
ovviamente fallisce con un errore fatale.
Per risolvere il problema di cui sopra, più altre risposte già menzionate nell'uso PL/pgSQL
, emettendo CREATE ROLE
incondizionatamente 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 ROLE
può generare anche altri errori e la simulazione IF NOT EXISTS
dovrebbe tacitare solo l'errore quando il ruolo esiste già.
CREATE ROLE
genera un duplicate_object
errore 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 EXISTS
comandi PostgreSQL si aggiungono , skipping
al loro messaggio, quindi per coerenza lo aggiungo anche qui.
Ecco il codice SQL completo per la simulazione CREATE ROLE IF NOT EXISTS
con 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