PostgreSQL nie obsługuje IF NOT EXISTSdo CREATE DATABASErachunku. Jest obsługiwany tylko w CREATE SCHEMA. Ponadto CREATE DATABASEnie może być wystawiony w transakcji, dlatego nie może być w DObloku z przechwytywaniem wyjątków.
Gdy CREATE SCHEMA IF NOT EXISTSzostanie wydany, a schemat już istnieje, zgłaszane jest powiadomienie (nie błąd) ze zduplikowanymi informacjami o obiekcie.
Aby rozwiązać te problemy, należy użyć dblinkrozszerzenia, które otwiera nowe połączenie z serwerem bazy danych i wykonuje zapytanie bez wchodzenia w transakcję. Możesz ponownie użyć parametrów połączenia, podając pusty ciąg.
Poniżej znajduje się PL/pgSQLkod, który w pełni symuluje CREATE DATABASE IF NOT EXISTSz takim samym zachowaniem jak w CREATE SCHEMA IF NOT EXISTS. Wywołuje CREATE DATABASEprzez dblink, catch duplicate_databasewyjątek (który jest wystawiany, gdy baza danych już istnieje) i przekształca go w powiadomienie z propagacją errcode. Wiadomość tekstowa została dołączona , skippingw taki sam sposób, jak to się dzieje CREATE SCHEMA IF NOT EXISTS.
CREATE EXTENSION IF NOT EXISTS dblink;
DO $$
BEGIN
PERFORM dblink_exec('', 'CREATE DATABASE testdb');
EXCEPTION WHEN duplicate_database THEN RAISE NOTICE '%, skipping', SQLERRM USING ERRCODE = SQLSTATE;
END
$$;
To rozwiązanie nie ma wyścigu, jak w innych odpowiedziach, gdzie baza danych może być utworzona przez proces zewnętrzny (lub inną instancję tego samego skryptu) pomiędzy sprawdzeniem, czy baza istnieje, a jej własnym utworzeniem.
Co więcej, gdy CREATE DATABASEnie powiedzie się z innym błędem niż baza danych już istnieje, ten błąd jest propagowany jako błąd i nie jest dyskretnie odrzucany. Jest tylko haczyk na duplicate_databasebłąd. Więc naprawdę zachowuje się tak, jak IF NOT EXISTSpowinien.
Możesz umieścić ten kod we własnej funkcji, wywołać go bezpośrednio lub z transakcji. Samo wycofanie (przywrócenie usuniętej bazy danych) nie zadziała.
Wyjście testowe (wywoływane dwa razy 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=# CREATE EXTENSION IF NOT EXISTS dblink;
CREATE EXTENSION
postgres=# DO $$
postgres$# BEGIN
postgres$# PERFORM dblink_exec('', 'CREATE DATABASE testdb');
postgres$# EXCEPTION WHEN duplicate_database THEN RAISE NOTICE '%, skipping', SQLERRM USING ERRCODE = SQLSTATE;
postgres$# END
postgres$# $$;
DO
postgres=#
postgres=# CREATE EXTENSION IF NOT EXISTS dblink;
NOTICE: 42710: extension "dblink" already exists, skipping
LOCATION: CreateExtension, extension.c:1539
CREATE EXTENSION
postgres=# DO $$
postgres$# BEGIN
postgres$# PERFORM dblink_exec('', 'CREATE DATABASE testdb');
postgres$# EXCEPTION WHEN duplicate_database THEN RAISE NOTICE '%, skipping', SQLERRM USING ERRCODE = SQLSTATE;
postgres$# END
postgres$# $$;
NOTICE: 42P04: database "testdb" already exists, skipping
LOCATION: exec_stmt_raise, pl_exec.c:3165
DO
postgres=#
postgres=# CREATE DATABASE testdb;
ERROR: 42P04: database "testdb" already exists
LOCATION: createdb, dbcommands.c:467
dblink_connect.