Redshift存储过程中如何处理Cedilla分隔符的COPY/UNLOAD命令
Redshift存储过程中使用八进制转义分隔符
\307的问题 问题背景
直接执行的UNLOAD语句能正常工作,指定的分隔符是Ç字符(用八进制转义\307表示):
unload('select * from table') to 's3://path' credentials '*******' header parallel off delimiter as '\307'
但把这段逻辑写成Redshift存储过程后,执行时抛出错误:BAD UTF8 hex sequence: C7 27 (error 4)。存储过程代码如下:
create or replace procedure unload_table(in path varchar) as $$ begin execute 'unload(''select * from table'') to ''||path||'' credentials '*******' header parallel off delimiter as ''\307'';'; end; $$ language plpgsql;
调用命令:
call unload_table('s3://path')
换成竖线|做分隔符时存储过程能正常跑,但业务必须用\307;直接写Ç字符会报错COPY delimiter must be a single one-byte character,之前用COPY命令时也碰到过同样问题,没法事后替换分隔符解决。
解决办法
问题核心是PL/pgSQL动态SQL的转义规则和直接执行SQL不同,这里提供两种可行方案:
方案1:双重转义八进制字符
在动态SQL字符串里,把\307改成\\307,同时修正原代码里路径拼接的引号错误(原写法会把||path||当成字符串的一部分,而不是变量拼接):
create or replace procedure unload_table(in path varchar) as $$ begin execute 'unload(''select * from table'') to ''' || path || ''' credentials ''*******'' header parallel off delimiter as ''\\307'';'; end; $$ language plpgsql;
原理:PL/pgSQL解析动态字符串时,会先把\\转成单个\,最终生成的执行语句里分隔符部分就是'\307',和直接执行的语句完全一致。
方案2:用CHR()函数构造分隔符
利用Redshift内置的CHR()函数,通过ASCII码(Ç的ASCII码是199,对应八进制307)直接生成目标字符,搭配format()函数自动处理字符串转义和引号,彻底避开转义问题:
create or replace procedure unload_table(in path varchar) as $$ begin execute format( 'unload(''select * from table'') to %L credentials %L header parallel off delimiter as %L;', path, '*******', CHR(199) ); end; $$ language plpgsql;
原理:%L是format()函数的占位符,会自动为字符串添加单引号并处理内部转义;CHR(199)生成的是单字节的Ç字符,符合Redshift对分隔符的要求。
注意事项
- 确认执行存储过程的会话字符集支持单字节ASCII,避免UTF-8环境下的字符解析异常
- 可以先执行
SELECT CHR(199);验证生成的字符是否正确
内容的提问来源于stack exchange,提问作者Rikky Bhai
相关产品推荐
相关产品推荐

