PostgreSQL 不支持IF NOT EXISTS
forCREATE DATABASE
语句。它仅在CREATE SCHEMA
. 此外CREATE DATABASE
,不能在事务中发出,因此它不能在DO
异常捕获的情况下阻塞。
当CREATE SCHEMA IF NOT EXISTS
发出并且模式已经存在时,会引发带有重复对象信息的通知(不是错误)。
要解决这些问题,您需要使用dblink
扩展来打开与数据库服务器的新连接并在不进入事务的情况下执行查询。您可以通过提供空字符串重用连接参数。
下面是PL/pgSQL
完全模拟CREATE DATABASE IF NOT EXISTS
与 in 相同行为的代码CREATE SCHEMA IF NOT EXISTS
。CREATE DATABASE
它通过调用dblink
,捕获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