PostgreSQL 数据库如何在两个连续的 shell 命令之间停止存在?

How can a PostgreSQL database cease to exist in between two consecutive shell commands?

我正在尝试在 CI(GitLab CI,如果相关)中创建一个 PostgreSQL 数据库 accounts_db,但前提是该数据库尚不存在。由于本机 Postgres 不支持它,我目前通过使用 psql 运行在 pg_database 上设置 SELECT 来解决它,只有当没有 return 结果时,我才使用psql 再次 运行 a CREATE DATABASE:

psql -tc "SELECT 1 FROM pg_database WHERE datname = 'accounts_db';" | grep -q 1 || psql -c "CREATE DATABASE accounts_db;"

这在大多数情况下有效:accounts_db 已经存在,因此 grep 成功退出并且 CREATE DATABASE 未执行。

不过,大多数时候并非总是如此。由于某种原因,它有时会在 || 的第二部分结束,只是因为数据库已经存在而出错:

$ psql -tc "SELECT 1 FROM pg_database WHERE datname = 'accounts_db';" | grep -q 1 || psql -c "CREATE DATABASE accounts_db;"
ERROR:  database "accounts_db" already exists

这怎么可能?

顺便说一句:你不需要 grep;您可以使用 psql 的退出代码,只需尝试连接到 new 数据库: (psql 以非零退出值退出的原因更多;但在那种情况下,第二个 psql 也会失败)

#!/bin/sh

THE_NAME="omg_wtf"

psql -U postgres ${THE_NAME} -tc "select 'yes';" || psql -U postgres postgres -tc "CREATE DATABASE ${THE_NAME} ;"

#Eof

但更简单:只需尝试创建数据库,如果失败则退出脚本:


#!/bin/sh

THE_NAME="omg_wtf"

psql -U postgres postgres -tc "CREATE DATABASE ${THE_NAME} ;"|| exit 1

# you wont get here if the above exited
echo "Created ${THE_NAME}"

#Eof

您是否混淆了真实的数据库名称,而真实的数据库名称有时包含大小写混合?

因为

SELECT 1 FROM pg_database WHERE datname = 'MyDB'

将匹配名为 MyDB 的数据库,但不会匹配名为 mydb 的数据库。但是,如果您 创建 数据库,您将得到一个大小写折叠的名称;例如

CREATE DATABASE MyDB;

创建一个名为 mydb 的数据库。因此,在这种情况下,您的存在测试将报告没有名为 MyDB 的数据库,然后您将去创建它,尝试创建 mydb 如果它已经存在则失败。

修复方法是使用标识符引用:

CREATE DATABASE "MyDB";

在那里保留大小写。或者,将 pg_database 的查询大小写折叠为小写:

SELECT 1 FROM pg_database WHERE datname = lower('MyDB')

...假设您知道您只会尝试以小写形式创建它。

(如果用户决定向您的脚本提供标识符引用的输入,那就更令人兴奋了...)