В некоторых ответах предлагалось использовать шаблон: проверьте, не существует ли роли, а если нет, выполните CREATE ROLE
команду. У этого есть один недостаток: состояние гонки. Если кто-то другой создает новую роль между проверкой и выдачей CREATE ROLE
команды, то, CREATE ROLE
очевидно, происходит сбой с фатальной ошибкой.
Чтобы решить вышеупомянутую проблему, в других ответах уже упоминалось использование PL/pgSQL
, CREATE ROLE
безоговорочная выдача, а затем перехват исключений из этого вызова. У этих решений есть только одна проблема. Они молча отбрасывают любые ошибки, в том числе те, которые не связаны с тем, что роль уже существует. CREATE ROLE
может выдавать также другие ошибки, и симуляция IF NOT EXISTS
должна заглушать только ошибку, когда роль уже существует.
CREATE ROLE
выдает duplicate_object
ошибку, когда роль уже существует. И обработчик исключений должен улавливать только эту одну ошибку. Как упоминалось в других ответах, рекомендуется преобразовать фатальную ошибку в простое уведомление. Другие IF NOT EXISTS
команды PostgreSQL добавляют , skipping
в свои сообщения, поэтому для единообразия я добавляю их и здесь.
Вот полный код SQL для моделирования CREATE ROLE IF NOT EXISTS
с правильным исключением и распространением sqlstate:
DO $$
BEGIN
CREATE ROLE test;
EXCEPTION WHEN duplicate_object THEN RAISE NOTICE '%, skipping', SQLERRM USING ERRCODE = SQLSTATE;
END
$$;
Тестовый вывод (вызывается дважды через DO, а затем напрямую):
$ 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