sql - 如果不存在则创建 PostgreSQL ROLE (user)

标签 sql postgresql roles dynamic-sql

我如何编写一个 SQL 脚本来在 PostgreSQL 9.1 中创建一个 ROLE,但如果它已经存在则不引发错误?

当前脚本只有:

CREATE ROLE my_user LOGIN PASSWORD 'my_password';

如果用户已经存在则失败。我想要这样的东西:

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

...但这不起作用 - IF 似乎在纯 SQL 中不受支持。

我有一个创建 PostgreSQL 9.1 数据库、角色和其他一些东西的批处理文件。它调用 psql.exe,传入要运行的 SQL 脚本的名称。到目前为止,所有这些脚本都是纯 SQL,如果可能的话,我想避免使用 PL/pgSQL 等。

最佳答案

简单脚本(提问)

基于 @a_horse_with_no_name的答案并改进了 @Gregory's comment :

DO
$do$
BEGIN
   IF EXISTS (
      SELECT FROM pg_catalog.pg_roles
      WHERE  rolname = 'my_user') THEN

      RAISE NOTICE 'Role "my_user" already exists. Skipping.';
   ELSE
      CREATE ROLE my_user LOGIN PASSWORD 'my_password';
   END IF;
END
$do$;

例如,与 CREATE TABLE 不同CREATE ROLE 没有 IF NOT EXISTS 子句(至少 Postgres 14)。而且您不能在纯 SQL 中执行动态 DDL 语句。

除非使用另一个 PL,否则您“避免使用 PL/pgSQL”的请求是不可能的。 DO statement使用 PL/pgSQL 作为默认过程语言:

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

无竞争条件

上述简单的解决方案允许在查找角色和创建角色之间的微小时间范围内出现竞争条件。如果一个并发事务在两者之间创建角色,我们毕竟会得到一个异常。在大多数工作负载中,这永远不会发生,因为创建角色是管理员很少执行的操作。但是有很多有争议的工作负载,例如 @blubb mentioned .
@Pali 添加了一个捕获异常的解决方案。但是带有 EXCEPTION 子句的代码块是昂贵的。 The manual:

A block containing an EXCEPTION clause is significantly more expensive to enter and exit than a block without one. Therefore, don't use EXCEPTION without need.

实际上引发异常(然后捕获它)的代价相对较高。所有这些只对经常执行它的工作负载很重要——这恰好是主要目标受众。优化:

DO
$do$
BEGIN
   IF EXISTS (
      SELECT FROM pg_catalog.pg_roles
      WHERE  rolname = 'my_user') THEN

      RAISE NOTICE 'Role "my_user" already exists. Skipping.';
   ELSE
      BEGIN   -- nested block
         CREATE ROLE my_user LOGIN PASSWORD 'my_password';
      EXCEPTION
         WHEN duplicate_object THEN
            RAISE NOTICE 'Role "my_user" was just created by a concurrent transaction. Skipping.';
      END;
   END IF;
END
$do$;

便宜得多:

  • 如果角色已经存在,我们永远不会进入昂贵的代码块。

  • 如果我们输入昂贵的代码块,则只有在不太可能发生的竞争条件命中时,该角色才会存在。所以我们几乎从来没有真正引发异常(并捕获它)。

关于sql - 如果不存在则创建 PostgreSQL ROLE (user),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8092086/

相关文章:

asp.net-mvc-3 - 使用 Intranet 应用程序实现用户角色的最佳方法

postgresql - 使用 Postgres 角色系统进行 Web 应用程序身份验证和角色管理是否合法?

php - SQL where 子句不起作用

sql - PostgreSQL - 条件排序

sql - 可修改的连接 View 是合理的设计选择吗?

postgresql - 连接到 Postgresql 数据库并执行 SQL 命令时调用 Sqlcmd 失败

php - 构建基于角色的应用程序

使用本地数据库的 C# SQL 随机样本

sql - 将数据从一个表移动到另一个表,postgresql 版本

django - 在单独的 VM 上使用应用程序和数据库简化 Azure 设置