Créer un rôle PostgreSQL (utilisateur) s'il n'existe pas

Comment écrire un script SQL pour créer un rôle dans PostgreSQL 9.1, mais sans créer d'erreur s'il existe déjà?

le script actuel a simplement:"

CREATE ROLE my_user LOGIN PASSWORD 'my_password';

Cela échoue si l'utilisateur existe déjà. J'aimerais quelque chose comme:

IF NOT EXISTS (SELECT * FROM pg_user WHERE username = 'my_user')
BEGIN
    CREATE ROLE my_user LOGIN PASSWORD 'my_password';
END;

... mais cela ne fonctionne pas - IF ne semble pas être supporté en SQL simple.

j'ai un fichier batch qui crée un PostgreSQL 9.1 base de données, rôle et quelques autres choses. Il appelle psql.exe, en passant au nom d'un script SQL à exécuter. Jusqu'à présent, tous ces scripts sont des SQL simples et j'aimerais éviter PL/pgSQL et autres, si possible.

71
demandé sur Erwin Brandstetter 2011-11-11 13:39:27

8 réponses

Simplifier d'une manière similaire à ce que vous aviez à l'esprit:

DO
$do$
BEGIN
   IF NOT EXISTS (
      SELECT                       -- SELECT list can stay empty for this
      FROM   pg_catalog.pg_roles
      WHERE  rolname = 'my_user') THEN

      CREATE ROLE my_user LOGIN PASSWORD 'my_password';
   END IF;
END
$do$;

(sur @a_horse_with_no_name 's réponse et amélioré après @Grégoire le commentaire de .)

contrairement, par exemple, à CREATE TABLE il n'y a pas de clause IF NOT EXISTS pour CREATE ROLE (pourtant). Et vous ne peut pas exécuter dynamique Les déclarations DDL en SQL simple.

votre requête pour "éviter PL / pgSQL" est impossible sauf en utilisant un autre PL. Le DO statement utilise plpgsql comme langage de procédure par défaut. La syntaxe permet d'omettre la déclaration explicite:

DO [ LANGUAGE lang_name ] code

...

lang_name

Le nom du langage procédural dans lequel le code est écrit. Si omise, la valeur par défaut est plpgsql .

101
répondu Erwin Brandstetter 2018-03-05 09:27:54

ou si le rôle n'est pas le propriétaire de tout objet db on peut utiliser:

DROP ROLE IF EXISTS my_user;
CREATE ROLE my_user LOGIN PASSWORD 'my_password';

mais seulement si l'abandon de cet utilisateur ne fera pas de mal.

33
répondu Borys 2012-12-13 16:09:14

Voici une solution générique utilisant plpgsql:

CREATE OR REPLACE FUNCTION create_role_if_not_exists(rolename NAME) RETURNS TEXT AS
$$
BEGIN
    IF NOT EXISTS (SELECT * FROM pg_roles WHERE rolname = rolename) THEN
        EXECUTE format('CREATE ROLE %I', rolename);
        RETURN 'CREATE ROLE';
    ELSE
        RETURN format('ROLE ''%I'' ALREADY EXISTS', rolename);
    END IF;
END;
$$
LANGUAGE plpgsql;

Utilisation:

posgres=# SELECT create_role_if_not_exists('ri');
 create_role_if_not_exists 
---------------------------
 CREATE ROLE
(1 row)
posgres=# SELECT create_role_if_not_exists('ri');
 create_role_if_not_exists 
---------------------------
 ROLE 'ri' ALREADY EXISTS
(1 row)
8
répondu ifischer 2014-02-20 12:38:18

comme vous êtes sur la 9.x, vous pouvez envelopper cela dans une déclaration de DO:

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$
;
7
répondu a_horse_with_no_name 2011-11-11 10:55:17

mon équipe rencontrait une situation avec plusieurs bases de données sur un serveur, en fonction de la base de données à laquelle vous vous êtes connecté, le rôle en question n'a pas été retourné par SELECT * FROM pg_catalog.pg_user , comme proposé par @erwin-brandstetter et @a_horse_with_no_name. Le bloc conditionnel exécuté, et nous avons frappé role "my_user" already exists .

Malheureusement, nous ne sommes pas sûrs des conditions exactes, mais cette solution fonctionne autour du problème:

        DO  
        $body$
        BEGIN
            CREATE ROLE my_user LOGIN PASSWORD 'my_password';
        EXCEPTION WHEN others THEN
            RAISE NOTICE 'my_user role exists, not re-creating';
        END
        $body$

Il pourrait probablement être fait plus précis pour exclure d'autres exceptions.

6
répondu Chris Betti 2016-08-15 22:35:56

Bash alternative (pour 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';"

(n'est pas la réponse à la question! c'est seulement pour ceux qui peuvent être utiles)

5
répondu Eduardo Cuomo 2018-04-05 12:28:27

la réponse acceptée souffre d'une condition de course si deux scripts de ce type sont exécutés simultanément sur le même cluster de Postgres (serveur DB), comme c'est commun dans les environnements d'intégration continue .

il est généralement plus sûr d'essayer de créer le rôle et de traiter avec élégance les problèmes lors de sa création:

DO $$
BEGIN
  CREATE ROLE my_role WITH NOLOGIN;
  EXCEPTION WHEN OTHERS THEN
  RAISE NOTICE 'not creating role my_role -- it already exists';
END
$$;
5
répondu blubb 2018-04-16 13:42:44

vous pouvez le faire dans votre fichier batch en analysant la sortie de:

SELECT * FROM pg_user WHERE usename = 'my_user'

puis psql.exe encore une fois si le rôle n'existe pas.

3
répondu Sheva 2016-12-12 19:18:09