Algunas respuestas sugirieron usar patrón: verifique si el rol no existe y si no, emita el CREATE ROLE
comando. Esto tiene una desventaja: condición de carrera. Si alguien más crea un nuevo rol entre la verificación y la emisión del CREATE ROLE
comando,CREATE ROLE
obviamente falla con un error fatal.
Para resolver el problema anterior, más otras respuestas ya mencionaron el uso de PL/pgSQL
, emitiendo CREATE ROLE
incondicionalmente y luego capturando excepciones de esa llamada. Solo hay un problema con estas soluciones. Eliminan silenciosamente cualquier error, incluidos los que no se generan por el hecho de que el rol ya existe. CREATE ROLE
puede arrojar también otros errores y la simulación IF NOT EXISTS
debe silenciar solo el error cuando el rol ya existe.
CREATE ROLE
arrojar duplicate_object
error cuando el rol ya existe. Y el controlador de excepciones debería detectar solo este error. Como se mencionó en otras respuestas, es una buena idea convertir un error fatal en un simple aviso. Se IF NOT EXISTS
agregan otros comandos de PostgreSQL, skipping
a su mensaje, por lo que para mantener la coherencia, también lo agrego aquí.
Aquí está el código SQL completo para la simulación CREATE ROLE IF NOT EXISTS
con la excepción correcta y la propagación de sqlstate:
DO $$
BEGIN
CREATE ROLE test;
EXCEPTION WHEN duplicate_object THEN RAISE NOTICE '%, skipping', SQLERRM USING ERRCODE = SQLSTATE;
END
$$;
Salida de prueba (llamada dos veces a través de DO y luego directamente):
$ 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