Деякі відповіді пропонують використовувати шаблон: перевірте, чи немає ролі, а якщо ні, то видайте 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