工作中有一个KingbaseES数据库,需要抽取数据转换为parquet格式用于分析。尝试使用duckdb的postgres_scanner扩展访问数据库,
D ATTACH 'host=localhost port=54321 dbname=test' AS kingbase (TYPE postgres);
D select * from kingbase.cards limit 10;
结果报如下错误:
Error: IO Error: Expected Postgres binary COPY header, got something else
首先想到postgres_scanner是开源软件,从源代码中查找报错信息,就知道报错原因了。
下载了一份源码,用UltraEdit在解压缩后的文件中查找上述字符串,很快找到了。
D:\postgres_scanner-main\src\include\postgres_binary_reader.hpp:
auto magic_len = PostgresConversion::COPY_HEADER_LENGTH;
...
if (memcmp(buffer_ptr, PostgresConversion::COPY_HEADER, magic_len) != 0) {
throw IOException("Expected Postgres binary COPY header, got something else");
上述代码表明,如果读到的buffer与COPY_HEADER字符串比较不相等,就抛出异常。再继续查找COPY_HEADER,在另一个头文件中有以下定义:
D:\postgres_scanner-main\src\include\postgres_conversion.hpp(102):
static constexpr const char *COPY_HEADER = "PGCOPY\n\377\r\n\0";
static constexpr const idx_t COPY_HEADER_LENGTH = 11;
综合以上信息可知,报错原因是这个插件使用了postgresql binary COPY,为了验证读的是postgresql数据库binary COPY流,将读到的内容与11个字节的标头比较,一致则继续读取。
而从KingbaseES数据库读取的标头不是上述内容,就退出了。
从没有报其他错误猜测,KingbaseES并没有修改其他内容,只修改了标头。
那么KingbaseES标头究竟是什么?只要生成一个binary COPY文件,打开看看就知道了。
在Postgresql和KingbaseES中分别建立了1个相同的表cards,在两个数据库中执行以下语句:
COPY cards to '/card2021.bin' WITH binary; --postgresql
COPY cards to '/card2022.bin' WITH binary; --KingbaseES
还是用UltraEdit打开上述bin文件,结果发现正如猜测,KingbaseES标头仅修改了前2个字节,由PG修改为KB,其余内容完全相同。
为了验证这一点,将KingbaseES生成的card2022.bin文件前2个字节修改为PG,导入postgresql数据库成功。
D:\>fc card2021.bin card2022.bin
正在比较文件 card2021.bin 和 CARD2022.BIN
FC: 找不到差异
COPY cards from '/card2022.bin' WITH binary;
原因找到,怎么解决?修改源代码再次编译应该可以,但需要搭建环境,猜测编译后的插件文件中也包括标头字符串,能否直接修改它完成目标?
结论是可以。
查询duckdb_extensions()得到插件路径和文件名,用UltraEdit搜索字符串PGCOPY并修改前2字节为KB后保存,然后用-unsigned参数打开duckdb:
D:\>duckdb\duckdb1 -unsigned
v1.0.0 1f98600c2c
Enter ".help" for usage hints.
D ATTACH 'host=localhost port=54321 dbname=test' AS kingbase (TYPE postgres);
D select * from kingbase.cards limit 10;
查询成功。
同事在Linux环境用Vim查找修改文件,也成功了。
这种简单粗暴的修改有个副作用,它不能读取postgresql和greenplum(包括人大金仓KADB)数据库了。
有更多需求的同事可以自行研究postgres_scanner源代码,修改后重新编译来实现(https://github.com/duckdb/postgres_scanner)。
工作中有一个KingbaseES数据库,需要抽取数据转换为
parquet格式用于分析。尝试使用duckdb的postgres_scanner扩展访问数据库,结果报如下错误:
首先想到postgres_scanner是开源软件,从源代码中查找报错信息,就知道报错原因了。
下载了一份源码,用UltraEdit在解压缩后的文件中查找上述字符串,很快找到了。
上述代码表明,如果读到的buffer与
COPY_HEADER字符串比较不相等,就抛出异常。再继续查找COPY_HEADER,在另一个头文件中有以下定义:综合以上信息可知,报错原因是这个插件使用了postgresql binary COPY,为了验证读的是postgresql数据库binary COPY流,将读到的内容与11个字节的标头比较,一致则继续读取。
而从KingbaseES数据库读取的标头不是上述内容,就退出了。
从没有报其他错误猜测,KingbaseES并没有修改其他内容,只修改了标头。
那么KingbaseES标头究竟是什么?只要生成一个binary COPY文件,打开看看就知道了。
在Postgresql和KingbaseES中分别建立了1个相同的表cards,在两个数据库中执行以下语句:
还是用UltraEdit打开上述bin文件,结果发现正如猜测,KingbaseES标头仅修改了前2个字节,由PG修改为KB,其余内容完全相同。
为了验证这一点,将KingbaseES生成的
card2022.bin文件前2个字节修改为PG,导入postgresql数据库成功。原因找到,怎么解决?修改源代码再次编译应该可以,但需要搭建环境,猜测编译后的插件文件中也包括标头字符串,能否直接修改它完成目标?
结论是可以。
查询
duckdb_extensions()得到插件路径和文件名,用UltraEdit搜索字符串PGCOPY并修改前2字节为KB后保存,然后用-unsigned参数打开duckdb:查询成功。
同事在Linux环境用Vim查找修改文件,也成功了。
这种简单粗暴的修改有个副作用,它不能读取postgresql和greenplum(包括人大金仓KADB)数据库了。
有更多需求的同事可以自行研究postgres_scanner源代码,修改后重新编译来实现(https://github.com/duckdb/postgres_scanner)。