建议使用模式的一些答案:检查角色是否不存在,如果不存在,则发出CREATE ROLE
命令。这有一个缺点:比赛条件。如果其他人在检查和发出CREATE ROLE
命令之间创建了新角色,则CREATE ROLE
显然会因致命错误而失败。
为了解决上述问题,已经提到了更多的其他答案PL/pgSQL
,CREATE ROLE
无条件地发出,然后从该调用中捕获异常。这些解决方案只有一个问题。他们会静默删除任何错误,包括那些由于角色已经存在而不会产生的错误。CREATE ROLE
可能还会引发其他错误,并且IF NOT EXISTS
当角色已经存在时,模拟应仅使错误静音。
CREATE ROLE
duplicate_object
当角色已经存在时抛出错误。并且异常处理程序应该只捕获这一错误。正如其他答案所述,将致命错误转换为简单通知是一个好主意。其他PostgreSQL IF NOT EXISTS
命令会添加, skipping
到其消息中,因此为了保持一致,我也在此处添加了它。
这是用于模拟CREATE ROLE IF NOT EXISTS
具有正确异常和sqlstate传播的完整SQL代码:
DO $$
BEGIN
CREATE ROLE test;
EXCEPTION WHEN duplicate_object THEN RAISE NOTICE '%, skipping', SQLERRM USING ERRCODE = SQLSTATE;
END
$$;
测试输出(通过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=# DO $$
postgres$# BEGIN
postgres$# CREATE ROLE test;
postgres$# EXCEPTION WHEN duplicate_object THEN RAISE NOTICE '%, skipping', SQLERRM USING ERRCODE = SQLSTATE;
postgres$# END
postgres$# $$;
DO
postgres=#
postgres=# DO $$
postgres$# BEGIN
postgres$# CREATE ROLE test;
postgres$# EXCEPTION WHEN duplicate_object THEN RAISE NOTICE '%, skipping', SQLERRM USING ERRCODE = SQLSTATE;
postgres$# END
postgres$# $$;
NOTICE: 42710: role "test" already exists, skipping
LOCATION: exec_stmt_raise, pl_exec.c:3165
DO
postgres=#
postgres=# CREATE ROLE test;
ERROR: 42710: role "test" already exists
LOCATION: CreateRole, user.c:337