Some answers suggest using a template: check if the role exists, and if not, CREATE ROLE the CREATE ROLE . This has one drawback: race condition. If someone else creates a new role between validation and the CREATE ROLE of the CREATE ROLE then the CREATE ROLE explicitly fails with a fatal error.
To solve the above problem, other answers already mentioned the use of PL/pgSQL , unconditionally CREATE ROLE and then CREATE ROLE exceptions from this call. There is only one problem with these solutions. They silently discard any errors, including those that are not caused by the fact that the role already exists. CREATE ROLE can generate other errors and simulations, IF NOT EXISTS should only ignore the error when the role already exists.
CREATE ROLE duplicate_object error when a role already exists. And the exception handler should catch only this one error. As mentioned in other answers, it is a good idea to convert a fatal error into a simple notification. Other PostgreSQL IF NOT EXISTS commands are added , skipping their messages, so for consistency I add them here as well.
Here is the complete SQL code for modeling CREATE ROLE IF NOT EXISTS with the correct exception and sqlstate propagation:
DO $$ BEGIN CREATE ROLE test; EXCEPTION WHEN duplicate_object THEN RAISE NOTICE '%, skipping', SQLERRM USING ERRCODE = SQLSTATE; END $$;
Test output (called twice through DO, and then directly):
$ 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
Pali May 2, '19 at 14:20 2019-05-02 14:20
source share