Quelques réponses suggèrent d'utiliser le modèle: vérifiez si le rôle n'existe pas et si ce n'est pas le cas, lancez la CREATE ROLE
commande. Cela a un inconvénient: la condition de course. Si quelqu'un d'autre crée un nouveau rôle entre la vérification et l'émission de la CREATE ROLE
commande, il CREATE ROLE
échoue évidemment avec une erreur fatale.
Pour résoudre le problème ci-dessus, d'autres réponses ont déjà mentionné l'utilisation de PL/pgSQL
, l'émission CREATE ROLE
inconditionnelle, puis la capture des exceptions de cet appel. Il n'y a qu'un seul problème avec ces solutions. Ils abandonnent silencieusement toutes les erreurs, y compris celles qui ne sont pas générées par le fait que ce rôle existe déjà. CREATE ROLE
peut également générer d'autres erreurs et la simulation IF NOT EXISTS
ne devrait faire taire l'erreur que lorsque le rôle existe déjà.
CREATE ROLE
lancer une duplicate_object
erreur lorsque le rôle existe déjà. Et le gestionnaire d'exceptions ne doit intercepter que cette seule erreur. Comme d'autres réponses l'ont mentionné, il est judicieux de convertir l'erreur fatale en simple avis. D'autres IF NOT EXISTS
commandes PostgreSQL s'ajoutent , skipping
à leur message, donc par souci de cohérence, je les ajoute ici aussi.
Voici le code SQL complet pour la simulation CREATE ROLE IF NOT EXISTS
avec l'exception correcte et la propagation sqlstate:
DO $$
BEGIN
CREATE ROLE test;
EXCEPTION WHEN duplicate_object THEN RAISE NOTICE '%, skipping', SQLERRM USING ERRCODE = SQLSTATE;
END
$$;
Sortie de test (appelée deux fois via DO puis directement):
$ 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