Algumas respostas sugeriram usar o padrão: verifique se a função não existe e se não, emita o CREATE ROLE
comando. Isso tem uma desvantagem: condição de corrida. Se outra pessoa criar uma nova função entre verificar e emitir o CREATE ROLE
comando, CREATE ROLE
obviamente falhará com um erro fatal.
Para resolver o problema acima, mais outras respostas já mencionaram o uso de PL/pgSQL
, emitindo CREATE ROLE
incondicionalmente e, em seguida, pegando exceções dessa chamada. Existe apenas um problema com essas soluções. Eles silenciosamente eliminam quaisquer erros, incluindo aqueles que não são gerados pelo fato de que a função já existe. CREATE ROLE
também pode lançar outros erros e a simulação IF NOT EXISTS
deve silenciar apenas o erro quando a função já existe.
CREATE ROLE
lançar duplicate_object
erro quando a função já existe. E o manipulador de exceções deve capturar apenas este erro. Como outras respostas mencionadas, é uma boa ideia converter o erro fatal em aviso simples. Outros IF NOT EXISTS
comandos PostgreSQL adicionam, skipping
à mensagem, portanto, para consistência, estou adicionando-os aqui também.
Aqui está o código SQL completo para simulação CREATE ROLE IF NOT EXISTS
com exceção correta e propagação sqlstate:
DO $$
BEGIN
CREATE ROLE test;
EXCEPTION WHEN duplicate_object THEN RAISE NOTICE '%, skipping', SQLERRM USING ERRCODE = SQLSTATE;
END
$$;
Saída de teste (chamado duas vezes via DO e depois diretamente):
$ 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