В некоторых ответах предлагается использовать шаблон: проверьте, не существует ли роль, а если нет, введите команду CREATE ROLE
. Это имеет один недостаток: состояние гонки. Если кто-то еще создает новую роль между проверкой и вводом команды CREATE ROLE
, то CREATE ROLE
явно завершается с фатальной ошибкой.
Чтобы решить вышеуказанную проблему, в других ответах уже упоминалось использование PL/pgSQL
, безоговорочно выдавая CREATE ROLE
и затем перехватывая исключения из этого вызова. Есть только одна проблема с этими решениями. Они молча отбрасывают любые ошибки, в том числе те, которые не вызваны тем, что роль уже существует. CREATE ROLE
может выдавать и другие ошибки, а симуляция IF NOT EXISTS
должна игнорировать только ошибку, когда роль уже существует.
CREATE ROLE
throw duplicate_object
Ошибка, когда роль уже существует. И обработчик исключений должен отлавливать только одну эту ошибку. Как уже упоминалось в других ответах, хорошей идеей является преобразование фатальной ошибки в простое уведомление. Другие команды PostgreSQL IF NOT EXISTS
добавляют , 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