psql脚本执行ALTER TABLE添加约束静默失败,终端执行却报错
让我来拆解你遇到的问题,核心原因和解决办法如下:
1. psql默认不终止于首个错误,退出码由最后一个命令决定
默认情况下,psql的ON_ERROR_STOP参数是关闭的。这意味着当你通过-c传递多条SQL命令时,哪怕第一条命令失败(比如email_non_zero_ln_check约束已经存在),psql会打印错误信息,但仍会继续执行后续的命令。
如果最后一条命令也失败(比如email_too_long_ln_check已经存在),psql会输出最后一条命令的错误,但最终的退出码是最后一条命令的状态码(非0)。但如果你没有主动检查退出码,或者错误信息被输出到了**stderr(标准错误流)**而你只关注了stdout(标准输出),就会误以为命令“成功”执行了。
2. 为什么没有返回错误码1并退出?
因为psql默认不会在遇到第一个错误时就终止执行。只有当你启用ON_ERROR_STOP选项后,psql才会在首个错误发生时立即退出,并返回对应的错误码。
解决办法
针对你的场景,有几个可行的方案:
方案一:启用ON_ERROR_STOP强制错误终止
在执行psql命令时添加-v ON_ERROR_STOP=1参数,这样只要有一条命令失败,psql就会立即退出,不再执行后续命令,同时返回非0错误码:
psql "$pg_cp_prod_conn_str" -v ON_ERROR_STOP=1 -c "$(<<EOF ALTER TABLE cp_user_emails ADD CONSTRAINT email_non_zero_ln_check CHECK (char_length(TRIM(email)) > 4); ALTER TABLE cp_user_emails ADD CONSTRAINT email_too_long_ln_check CHECK (char_length(TRIM(email)) < 321); EOF )"
同时建议把stderr重定向到stdout,确保能看到所有错误信息:
psql "$pg_cp_prod_conn_str" -v ON_ERROR_STOP=1 -c "$(<<EOF ALTER TABLE cp_user_emails ADD CONSTRAINT email_non_zero_ln_check CHECK (char_length(TRIM(email)) > 4); ALTER TABLE cp_user_emails ADD CONSTRAINT email_too_long_ln_check CHECK (char_length(TRIM(email)) < 321); EOF )" 2>&1
方案二:使用IF NOT EXISTS避免重复添加约束
如果你使用的是PostgreSQL 9.5或更高版本,可以用IF NOT EXISTS语法,这样当约束已经存在时,命令会静默跳过而不是报错:
psql "$pg_cp_prod_conn_str" -c "$(<<EOF ALTER TABLE cp_user_emails ADD CONSTRAINT IF NOT EXISTS email_non_zero_ln_check CHECK (char_length(TRIM(email)) > 4); ALTER TABLE cp_user_emails ADD CONSTRAINT IF NOT EXISTS email_too_long_ln_check CHECK (char_length(TRIM(email)) < 321); EOF )"
这种方式下,无论约束是否已经存在,命令都会返回成功状态码,且只会添加不存在的约束。
方案三:先排查已存在的约束
在执行脚本前,先在交互模式下查看cp_user_emails表的已有约束,确认哪些已经存在:
\d cp_user_emails
或者用SQL查询:
SELECT conname FROM pg_constraint WHERE conrelid = 'cp_user_emails'::regclass;
这样可以针对性地调整脚本,只添加缺失的约束。
内容的提问来源于stack exchange,提问作者user12834955

