PostgreSQL不支持IF NOT EXISTS
的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
异常(在数据库已经存在时发出)进行调用,并将其通过propagating转换为notice 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
。