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 相同。它通过dblink 调用CREATE DATABASE,捕获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