PostgreSQL не поддерживает оператор IF NOT EXISTS
for 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 DATABASE
via 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
.