PostgreSQL не поддерживает оператор IF NOT EXISTSfor CREATE DATABASE. Поддерживается только в CREATE SCHEMA. Более того, CREATE DATABASEне может быть оформлен в транзакции, поэтому не может быть в DOблоке с отловом исключения.
Когда CREATE SCHEMA IF NOT EXISTSвыдается и схема уже существует, появляется уведомление (а не ошибка) с повторяющейся информацией об объекте.
Чтобы решить эти проблемы, вам необходимо использовать dblinkрасширение, которое открывает новое соединение с сервером базы данных и выполняет запрос без входа в транзакцию. Вы можете повторно использовать параметры подключения, указав пустую строку.
Ниже приведен PL/pgSQLкод, который полностью имитирует CREATE DATABASE IF NOT EXISTSповедение, как в CREATE SCHEMA IF NOT EXISTS. Он вызывает исключение CREATE DATABASEvia dblink, catch duplicate_database(которое выдается, когда база данных уже существует) и преобразует его в уведомление с распространением errcode. Строковое сообщение добавлено так , skippingже, как и 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
$$;
Это решение не имеет какого-либо состояния гонки, как и в других ответах, где база данных может быть создана внешним процессом (или другим экземпляром того же сценария) между проверкой наличия базы данных и ее собственным созданием.
Более того, когда происходит CREATE DATABASEсбой с ошибкой, отличной от того, что база данных уже существует, эта ошибка распространяется как ошибка, а не игнорируется. Есть только ловушка для duplicate_databaseошибки. Так что действительно ведет себя как IF NOT EXISTSнадо.
Вы можете поместить этот код в собственную функцию, вызвать ее напрямую или из транзакции. Просто откат (восстановление удаленной базы данных) не сработает.
Тестирование вывода (вызывается дважды через 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=# 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.