O PostgreSQL não apoiar IF NOT EXISTS
para CREATE DATABASE
declaração. É compatível apenas com CREATE SCHEMA
. Além disso, CREATE DATABASE
não pode ser emitido na transação, portanto, não pode ser em DO
bloco com captura de exceção.
Quando CREATE SCHEMA IF NOT EXISTS
é emitido e o esquema já existe, é gerado um aviso (não erro) com informações de objeto duplicadas.
Para resolver esses problemas, você precisa usar a dblink
extensão que abre uma nova conexão com o servidor de banco de dados e executa a consulta sem entrar em transação. Você pode reutilizar os parâmetros de conexão fornecendo uma string vazia.
Abaixo está o PL/pgSQL
código que simula totalmente o CREATE DATABASE IF NOT EXISTS
mesmo comportamento do CREATE SCHEMA IF NOT EXISTS
. Ele chama CREATE DATABASE
via dblink
, catch duplicate_database
exception (que é emitido quando o banco de dados já existe) e o converte em aviso com propagação errcode
. A mensagem de string foi anexada , skipping
da mesma maneira que ela 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
$$;
Esta solução é sem nenhuma condição de corrida como nas outras respostas, onde o banco de dados pode ser criado por processo externo (ou outra instância do mesmo script) entre a verificação da existência do banco de dados e sua própria criação.
Além disso, quando CREATE DATABASE
falha com outro erro que não o banco de dados já existe, esse erro é propagado como erro e não descartado silenciosamente. Só existe captura para o duplicate_database
erro. Portanto, realmente se comporta como IF NOT EXISTS
deveria.
Você pode colocar este código em sua própria função, chamá-lo diretamente ou a partir da transação. Apenas a reversão (restauração do banco de dados eliminado) não funcionaria.
Saída de teste (chamada duas vezes via DO e depois diretamente):
$ 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
.