PostgreSQL nie obsługuje IF NOT EXISTS
do CREATE DATABASE
rachunku. Jest obsługiwany tylko w CREATE SCHEMA
. Ponadto CREATE DATABASE
nie może być wystawiony w transakcji, dlatego nie może być w DO
bloku z przechwytywaniem wyjątków.
Gdy CREATE SCHEMA IF NOT EXISTS
zostanie 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ć dblink
rozszerzenia, 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/pgSQL
kod, który w pełni symuluje CREATE DATABASE IF NOT EXISTS
z takim samym zachowaniem jak w CREATE SCHEMA IF NOT EXISTS
. Wywołuje CREATE DATABASE
przez dblink
, catch duplicate_database
wyją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 , skipping
w 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 DATABASE
nie 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_database
błąd. Więc naprawdę zachowuje się tak, jak IF NOT EXISTS
powinien.
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
.