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')
...假设您知道您只会尝试以小写形式创建它。
(如果用户决定向您的脚本提供标识符引用的输入,那就更令人兴奋了...)
我正在尝试在 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')
...假设您知道您只会尝试以小写形式创建它。
(如果用户决定向您的脚本提供标识符引用的输入,那就更令人兴奋了...)