O PostgreSQL não apoiar IF NOT EXISTSpara CREATE DATABASEdeclaração. É compatível apenas com CREATE SCHEMA. Além disso, CREATE DATABASEnão pode ser emitido na transação, portanto, não pode ser em DObloco 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 dblinkextensã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/pgSQLcódigo que simula totalmente o CREATE DATABASE IF NOT EXISTSmesmo comportamento do CREATE SCHEMA IF NOT EXISTS. Ele chama CREATE DATABASEvia dblink, catch duplicate_databaseexception (que é emitido quando o banco de dados já existe) e o converte em aviso com propagação errcode. A mensagem de string foi anexada , skippingda 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 DATABASEfalha 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_databaseerro. Portanto, realmente se comporta como IF NOT EXISTSdeveria.
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.