Niektóre odpowiedzi sugerowały użycie wzorca: sprawdź, czy rola nie istnieje, a jeśli nie, wydaj CREATE ROLE
polecenie. Ma to jedną wadę: stan wyścigu. Jeśli ktoś inny utworzy nową rolę między sprawdzeniem a wydaniem CREATE ROLE
poleceniaCREATE ROLE
oczywiście nie powiedzie się z błędem krytycznym.
Aby rozwiązać powyższy problem, więcej innych odpowiedzi wspominało już o użyciu PL/pgSQL
, wydawaniu CREATE ROLE
bezwarunkowym, a następnie wyłapywaniu wyjątków od tego połączenia. Z tymi rozwiązaniami jest tylko jeden problem. Po cichu odrzucają wszelkie błędy, w tym te, które nie są generowane przez fakt, że rola już istnieje. CREATE ROLE
może generować również inne błędy, a symulacja IF NOT EXISTS
powinna wyciszać błędy tylko wtedy, gdy rola już istnieje.
CREATE ROLE
zgłosić duplicate_object
błąd, gdy rola już istnieje. Program obsługi wyjątków powinien wychwycić tylko ten jeden błąd. Jak wspomniały inne odpowiedzi, dobrym pomysłem jest przekształcenie błędu krytycznego w proste powiadomienie. Inne IF NOT EXISTS
polecenia PostgreSQL są dodawane , skipping
do ich wiadomości, więc dla spójności dodam je tutaj.
Oto pełny kod SQL do symulacji CREATE ROLE IF NOT EXISTS
z poprawnym wyjątkiem i propagacją stanu sql :
DO $$
BEGIN
CREATE ROLE test;
EXCEPTION WHEN duplicate_object THEN RAISE NOTICE '%, skipping', SQLERRM USING ERRCODE = SQLSTATE;
END
$$;
Wyjście testowe (wywoływane dwukrotnie przez DO, a następnie bezpośrednio):
$ 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