Create PostgreSQL ROLE (user) if it doesn't exist

前端 未结 10 1734
旧时难觅i
旧时难觅i 2020-12-12 15:47

How do I write an SQL script to create a ROLE in PostgreSQL 9.1, but without raising an error if it already exists?

The current script simply has:

CR         


        
相关标签:
10条回答
  • 2020-12-12 16:19

    The accepted answer suffers from a race condition if two such scripts are executed concurrently on the same Postgres cluster (DB server), as is common in continuous-integration environments.

    It's generally safer to try to create the role and gracefully deal with problems when creating it:

    DO $$
    BEGIN
      CREATE ROLE my_role WITH NOLOGIN;
      EXCEPTION WHEN DUPLICATE_OBJECT THEN
      RAISE NOTICE 'not creating role my_role -- it already exists';
    END
    $$;
    
    0 讨论(0)
  • 2020-12-12 16:23

    You can do it in your batch file by parsing the output of:

    SELECT * FROM pg_user WHERE usename = 'my_user'
    

    and then running psql.exe once again if the role does not exist.

    0 讨论(0)
  • 2020-12-12 16:24

    As you are on 9.x, you can wrap that into a DO statement:

    do 
    $body$
    declare 
      num_users integer;
    begin
       SELECT count(*) 
         into num_users
       FROM pg_user
       WHERE usename = 'my_user';
    
       IF num_users = 0 THEN
          CREATE ROLE my_user LOGIN PASSWORD 'my_password';
       END IF;
    end
    $body$
    ;
    
    0 讨论(0)
  • 2020-12-12 16:26

    Some answers suggested to use pattern: check if role does not exist and if not then issue CREATE ROLE command. This has one disadvantage: race condition. If somebody else creates a new role between check and issuing CREATE ROLE command then CREATE ROLE obviously fails with fatal error.

    To solve above problem, more other answers already mentioned usage of PL/pgSQL, issuing CREATE ROLE unconditionally and then catching exceptions from that call. There is just one problem with these solutions. They silently drop any errors, including those which are not generated by fact that role already exists. CREATE ROLE can throw also other errors and simulation IF NOT EXISTS should silence only error when role already exists.

    CREATE ROLE throw duplicate_object error when role already exists. And exception handler should catch only this one error. As other answers mentioned it is a good idea to convert fatal error to simple notice. Other PostgreSQL IF NOT EXISTS commands adds , skipping into their message, so for consistency I'm adding it here too.

    Here is full SQL code for simulation of CREATE ROLE IF NOT EXISTS with 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 two times via 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
    
    0 讨论(0)
  • 2020-12-12 16:28

    Bash alternative (for Bash scripting):

    psql -h localhost -U postgres -tc \
    "SELECT 1 FROM pg_user WHERE usename = 'my_user'" \
    | grep -q 1 \
    || psql -h localhost -U postgres \
    -c "CREATE ROLE my_user LOGIN PASSWORD 'my_password';"
    

    (isn't the answer for the question! it is only for those who may be useful)

    0 讨论(0)
  • 2020-12-12 16:32

    Simplify in a similar fashion to what you had in mind:

    DO
    $do$
    BEGIN
       IF NOT EXISTS (
          SELECT FROM pg_catalog.pg_roles  -- SELECT list can be empty for this
          WHERE  rolname = 'my_user') THEN
    
          CREATE ROLE my_user LOGIN PASSWORD 'my_password';
       END IF;
    END
    $do$;
    

    (Building on @a_horse_with_no_name's answer and improved with @Gregory's comment.)

    Unlike, for instance, with CREATE TABLE there is no IF NOT EXISTS clause for CREATE ROLE (up to at least pg 12). And you cannot execute dynamic DDL statements in plain SQL.

    Your request to "avoid PL/pgSQL" is impossible except by using another PL. The DO statement uses plpgsql as default procedural language. The syntax allows to omit the explicit declaration:

    DO [ LANGUAGE lang_name ] code
    ...
    lang_name
    The name of the procedural language the code is written in. If omitted, the default is plpgsql.

    0 讨论(0)
提交回复
热议问题