为什么psql使用-f执行COPY FROM STDIN失败但-c执行成功
问题原理说明
psql处理-c(内联命令)和-f(脚本文件)时,对COPY FROM STDIN的输入源读取逻辑完全不同,这是报错的根本原因,和\copy功能直接相关:
核心机制铺垫
PostgreSQL有两种COPY用法:
- 服务器端SQL语法
COPY FROM STDIN:由服务器发起读取输入,默认预期输入来自连接的数据流,直到收到结束标记\.才终止COPY状态 - 客户端psql元命令
\copy FROM STDIN:由psql客户端接管读取逻辑,固定从psql的标准输入读取数据,读取完成后自动发送结束标记
两种执行模式的差异
1. -c模式执行正常的原因
通过$(cat testscript.pg)把整段SQL一次性作为参数传给了-c,psql会把整段SQL全部一次性发送给Postgres服务器执行:
- 服务器执行到
COPY FROM STDIN时,会开始从psql的标准输入(也就是通过管道传入的testdata.csv内容)读取数据 - CSV内容读取完成后,标准输入触发EOF,psql自动向服务器发送COPY结束信号,服务器退出COPY状态,继续执行后面的
select * from fruits语句,因此运行正常
2. -f模式报错的原因
psql读取本地脚本文件执行时,遵循脚本优先的输入规则:
- 遇到SQL级的
COPY FROM STDIN语句后,psql会直接从当前正在读取的脚本文件中继续读取后续行作为COPY的输入数据,而不是读取psql的标准输入 - 因此脚本中
COPY语句下一行的select * from fruits被直接当成COPY的输入发送给了服务器,服务器尝试把这行字符串转为整数类型的id字段,自然就抛出了类型不匹配的错误,通过管道传入的CSV数据全程没有被用到
解决方案
如果要让-f模式也能正常读取管道传入的CSV数据,只需要把脚本中的SQL级COPY语句替换为psql客户端级的\copy元命令即可:
-- 修改testscript.pg中的COPY行 \copy fruits from stdin with delimiter as ',' csv;
修改后-f执行时,psql会按照\copy的规则从标准输入读取CSV数据,两种执行模式的表现就会完全一致。
内容的提问来源于stack exchange,提问作者Neil C. Obremski
相关产品推荐
相关产品推荐

