如果 PostgreSQL ROLE(用户)不存在,则创建它

发布于 2024-12-15 05:44:31 字数 514 浏览 3 评论 0原文

如何编写 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 等。

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:

CREATE ROLE my_user LOGIN PASSWORD 'my_password';

This fails if the user already exists. I'd like something like:

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

... but that doesn't work - IF doesn't seem to be supported in plain SQL.

I have a batch file that creates a PostgreSQL 9.1 database, role and a few other things. It calls psql.exe, passing in the name of an SQL script to run. So far all these scripts are plain SQL and I'd like to avoid PL/pgSQL and such, if possible.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(14

嘴硬脾气大 2024-12-22 05:44:31

简单脚本(提出问题)

基于 @a_horse_with_no_name 的答案构建,并使用 @Gregory 的评论

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(至少 Postgres 14)。并且您无法以普通 SQL 执行动态 DDL 语句。

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

DO [ LANGUAGE lang_name ] 代码
...
lang_name
编写代码所用的过程语言的名称。如果
省略,默认为plpgsql

无竞争条件

上述简单的解决方案允许在查找角色和创建角色之间的极短时间范围内出现竞争条件。如果并发事务在两者之间创建角色,我们最终会得到一个异常。在大多数工作负载中,这种情况永远不会发生,因为创建角色是管理员很少执行的操作。但也存在一些极具争议性的工作负载,例如提到的@blubb.
@Pali 添加了一个捕获异常的解决方案。但是带有 EXCEPTION 子句的代码块的成本很高。
手册:

包含 EXCEPTION 子句的块明显更多
进入和退出比没有进入和退出的街区昂贵。因此,不要
无需使用EXCEPTION

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

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$;

更便宜:

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

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

Simple script (question asked)

Building on @a_horse_with_no_name's answer and improved with @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$;

Unlike, for instance, with CREATE TABLE there is no IF NOT EXISTS clause for CREATE ROLE (up to at least Postgres 14). 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 PL/pgSQL as default procedural language:

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

No race condition

The above simple solution allows for a race condition in the tiny time frame between looking up the role and creating it. If a concurrent transaction creates the role in between we get an exception after all. In most workloads, that will never happen as creating roles is a rare operation carried out by an admin. But there are highly contentious workloads like @blubb mentioned.
@Pali added a solution trapping the exception. But a code block with an EXCEPTION clause is expensive. 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.

Actually raising an exception (and then trapping it) is comparatively expensive on top of it. All of this only matters for workloads that execute it a lot - which happens to be the primary target audience. To optimize:

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$;

Much cheaper:

  • If the role already exists, we never enter the expensive code block.

  • If we enter the expensive code block, the role only ever exists if the unlikely race condition hits. So we hardly ever actually raise an exception (and catch it).

盗琴音 2024-12-22 05:44:31

一些答案建议使用模式:检查角色是否不存在,如果不存在则发出 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 的完整 SQL 代码,具有正确的异常和 sqlstate 传播:

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

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
萌辣 2024-12-22 05:44:31

或者,如果该角色不是任何可以使用的数据库对象的所有者:

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

但前提是删除该用户不会造成任何损害。

Or if the role is not the owner of any db objects one can use:

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

But only if dropping this user will not make any harm.

○闲身 2024-12-22 05:44:31

Bash 替代方案(对于 Bash 脚本):(

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';"

不是问题的答案!它仅适用于那些可能有用的人)

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)

那伤。 2024-12-22 05:44:31

这是使用 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;

用法:

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)

Here is a generic solution using 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;

Usage:

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)
断爱 2024-12-22 05:44:31

Simulate CREATE DATABASE IF NOT EXISTS for PostgreSQL? 相同的解决方案应该可以工作 - 发送 CREATE USER ...\gexec

psql 中的解决方法

SELECT 'CREATE USER my_user'
WHERE NOT EXISTS (SELECT FROM pg_catalog.pg_roles WHERE rolname = 'my_user')\gexec

来自 shell 的解决方法

echo "SELECT 'CREATE USER my_user' WHERE NOT EXISTS (SELECT FROM pg_catalog.pg_roles WHERE rolname = 'my_user')\gexec" | psql

请参阅那里接受的答案了解更多详细信息。

The same solution as for Simulate CREATE DATABASE IF NOT EXISTS for PostgreSQL? should work - send a CREATE USER … to \gexec.

Workaround from within psql

SELECT 'CREATE USER my_user'
WHERE NOT EXISTS (SELECT FROM pg_catalog.pg_roles WHERE rolname = 'my_user')\gexec

Workaround from the shell

echo "SELECT 'CREATE USER my_user' WHERE NOT EXISTS (SELECT FROM pg_catalog.pg_roles WHERE rolname = 'my_user')\gexec" | psql

See accepted answer there for more details.

2024-12-22 05:44:31

根据此处的其他答案,我希望能够针对 .sql 文件执行一次 psql ,以使其执行一组初始化操作。我还希望能够在执行时注入密码以支持 CI/CD 场景。

-- init.sql

CREATE OR REPLACE FUNCTION pg_temp.create_myuser(theUsername text, thePassword text)
RETURNS void AS
$BODY$
DECLARE
  duplicate_object_message text;
BEGIN
  BEGIN
    EXECUTE format(
      'CREATE USER %I WITH PASSWORD %L',
      theUsername,
      thePassword
    );
  EXCEPTION WHEN duplicate_object THEN
    GET STACKED DIAGNOSTICS duplicate_object_message = MESSAGE_TEXT;
    RAISE NOTICE '%, skipping', duplicate_object_message;
  END;
END;
$BODY$
LANGUAGE 'plpgsql';

SELECT pg_temp.create_myuser(:'vUsername', :'vPassword');

使用 psql 调用:

NEW_USERNAME="my_new_user"
NEW_PASSWORD="password with 'special' characters"

psql --no-psqlrc --single-transaction --pset=pager=off \
  --tuples-only \
  --set=ON_ERROR_STOP=1 \
  --set=vUsername="$NEW_USERNAME" \
  --set=vPassword="$NEW_PASSWORD" \
  -f init.sql

这将允许 init.sql 在本地或通过 CI/CD 管道运行。


注意:

  • 我没有找到直接在 DO 匿名函数中引用文件变量 (:vPassword) 的方法,因此完整的 FUNCTION 为传递参数。 (请参阅@Clodoaldo Neto 的回答
  • @Erwin Brandstetter 的回答解释了为什么我们必须使用 EXECUTE 而不能使用 CREATE直接用户
  • @Pali 的回答解释了 EXCEPTION 防止竞争条件的必要性(这就是为什么 <不推荐使用 code>\gexec 方法)。
  • 该函数必须在 SELECT 语句中调用。使用 psql 命令中的 -t/--tuples-only 属性来清理日志输出,如 @villy393 的回答
  • 该函数是在临时模式中创建的,因此它将被自动删除。
  • 引用处理得当,因此密码中的特殊字符不会导致错误或更糟糕的安全漏洞。

Building off of the other answers here, I wanted the ability to execute psql once against a .sql file to have it perform a set of initialization operations. I also wanted the ability to inject the password at the time of execution to support CI/CD scenarios.

-- init.sql

CREATE OR REPLACE FUNCTION pg_temp.create_myuser(theUsername text, thePassword text)
RETURNS void AS
$BODY$
DECLARE
  duplicate_object_message text;
BEGIN
  BEGIN
    EXECUTE format(
      'CREATE USER %I WITH PASSWORD %L',
      theUsername,
      thePassword
    );
  EXCEPTION WHEN duplicate_object THEN
    GET STACKED DIAGNOSTICS duplicate_object_message = MESSAGE_TEXT;
    RAISE NOTICE '%, skipping', duplicate_object_message;
  END;
END;
$BODY$
LANGUAGE 'plpgsql';

SELECT pg_temp.create_myuser(:'vUsername', :'vPassword');

Invoking with psql:

NEW_USERNAME="my_new_user"
NEW_PASSWORD="password with 'special' characters"

psql --no-psqlrc --single-transaction --pset=pager=off \
  --tuples-only \
  --set=ON_ERROR_STOP=1 \
  --set=vUsername="$NEW_USERNAME" \
  --set=vPassword="$NEW_PASSWORD" \
  -f init.sql

This will allow init.sql to be run either locally or by the CI/CD pipeline.


Notes:

  • I did not find a way to reference a file variable (:vPassword) directly in a DO anonymous function, hence the full FUNCTION to pass the arg. (see @Clodoaldo Neto's answer)
  • @Erwin Brandstetter's answer explains why we must use an EXECUTE and cannot use CREATE USER directly.
  • @Pali's answer explains the need for the EXCEPTION to prevent race conditions (which is why the \gexec approach is not recommended).
  • The function must be invoked in a SELECT statement. Use the -t/--tuples-only attribute in the psql command to clean up log output, as pointed out in @villy393's answer.
  • The function is created in a temporary schema, so it will be deleted automatically.
  • Quoting is handled properly, so no special character in password can cause errors or worse, security vulnerability.
梦在深巷 2024-12-22 05:44:31

我的团队遇到了一台服务器上有多个数据库的情况,根据您连接到的数据库,SELECT * FROM pg_catalog.pg_user 没有返回有问题的角色,正如 @erwin-brandstetter 提议的那样和@a_horse_with_no_name。条件块被执行,我们命中角色“my_user”已经存在

不幸的是,我们不确定确切的条件,但这个解决方案可以解决这个问题:

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$

它可能会变得更加具体,以排除其他异常。

My team was hitting a situation with multiple databases on one server, depending on which database you connected to, the ROLE in question was not returned by SELECT * FROM pg_catalog.pg_user, as proposed by @erwin-brandstetter and @a_horse_with_no_name. The conditional block executed, and we hit role "my_user" already exists.

Unfortunately we aren't sure of exact conditions, but this solution works around the problem:

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$

It could probably be made more specific to rule out other exceptions.

爱她像谁 2024-12-22 05:44:31

当您使用 9.x 时,您可以将其包装到 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$
;

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$
;
笨死的猪 2024-12-22 05:44:31

如果您有权访问 shell,则可以执行此操作。

psql -tc "SELECT 1 FROM pg_user WHERE usename = 'some_use'" | grep -q 1 || psql -c "CREATE USER some_user"

对于那些想要解释的人:

-c = run command in database session, command is given in string
-t = skip header and footer
-q = silent mode for grep 
|| = logical OR, if grep fails to find match run the subsequent command

If you have access to a shell, you can do this.

psql -tc "SELECT 1 FROM pg_user WHERE usename = 'some_use'" | grep -q 1 || psql -c "CREATE USER some_user"

For those of you who would like an explanation:

-c = run command in database session, command is given in string
-t = skip header and footer
-q = silent mode for grep 
|| = logical OR, if grep fails to find match run the subsequent command
似梦非梦 2024-12-22 05:44:31

您可以在批处理文件中通过解析以下输出来完成此操作:

SELECT * FROM pg_user WHERE usename = 'my_user'

如果角色不存在,则再次运行 psql.exe

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.

忆悲凉 2024-12-22 05:44:31

我需要在 Makefile 中使用此命令,以便在用户存在时不会使作业失败:

initdb:
    psql postgres -c "CREATE USER foo CREATEDB PASSWORD 'bar'" || true
    ...

I needed this in a Makefile to not fail the job when the user exists:

initdb:
    psql postgres -c "CREATE USER foo CREATEDB PASSWORD 'bar'" || true
    ...
泅渡 2024-12-22 05:44:31

如果有人有兴趣在 Postgres 中使用 Liquibase 创建角色(如果该角色尚不存在):

<databaseChangeLog
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
        xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
            http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">

    <changeSet id="create-role" author="me" dbms="postgresql">
        <preConditions onFail="MARK_RAN">
            <sqlCheck expectedResult="0">SELECT COUNT(*) FROM pg_catalog.pg_roles WHERE rolname = 'role_name'</sqlCheck>
        </preConditions>
        <sql dbms="postgresql">
            CREATE ROLE role_name;
        </sql>
    </changeSet>

</databaseChangeLog>

If anyone is interested in creating a role using Liquibase in Postgres if it does not exist already:

<databaseChangeLog
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
        xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
            http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">

    <changeSet id="create-role" author="me" dbms="postgresql">
        <preConditions onFail="MARK_RAN">
            <sqlCheck expectedResult="0">SELECT COUNT(*) FROM pg_catalog.pg_roles WHERE rolname = 'role_name'</sqlCheck>
        </preConditions>
        <sql dbms="postgresql">
            CREATE ROLE role_name;
        </sql>
    </changeSet>

</databaseChangeLog>
朱染 2024-12-22 05:44:31
DO $
    BEGIN
        IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = '<role_name>') THEN
            CREATE ROLE <role_name>;
        END IF;
    END
$

说明:

  • DO $$ ... $$;:此块允许您执行 PL/pgSQL 代码块,而无需创建永久的功能。
  • IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = '') THEN:此命令检查是否有名为 ROLE >已存在于数据库中。如果不存在,则执行 CREATE ROLE; 语句。
DO $
    BEGIN
        IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = '<role_name>') THEN
            CREATE ROLE <role_name>;
        END IF;
    END
$

Explanation:

  • DO $$ ... $$;: This block allows you to execute a block of PL/pgSQL code without creating a permanent function.
  • IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = '<role_name>') THEN: This command checks if a ROLE named <role_name> already exists in the database. If it doesn't exist, the CREATE ROLE <role_name>; statement is executed.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文