duckdb_fdw 支持Postgres 17 与 DuckDB 1.1.2了
PostgreSQL 17 与 DuckDB 1.1.2 都出来很久了,我的新版本 duckdb_fdw 才姗姗来迟。更新 DuckDB 到V1.1.2 版本没有碰到问题。更新PG 到17碰到了些小问题。
下面简单介绍下编译PG 17支持版本碰到的问题
tuplestore_donestoring
PG 17 去除了 tuplestore_donestoring
函数,搜索了下PG 16里的 tuplestore_donestoring
实现,
# tuplestore.h
/* Backwards compatibility macro */
#define tuplestore_donestoring(state) ((void) 0)
出于好奇,我想看看它是从哪个版本开始就不再有意义,经过一番搜索发现,从大佬 Tom Lane 2003-03-09 的 commit( dd04e958c8b03c0f0512497651678c7816af3198[1]) 开始,
tuplestore_donestoring() isn't needed anymore, but provide a no-op
macro definition so as not to create compatibility problems.

该 commit 对应的tag是7.4
git tag --contains dd04e958c8b03c0f0512497651678c7816af3198
#返回
REL7_4
REL7_4_1
...

原来tuplestore_donestoring
是一个从FDW诞生之日起就没意义的函数,那又是谁引入的这个无用的函数的呢(我是抄的 sqlite_fdw),继续挖掘,找到了,是 Martin Pihlak 和 Peter Eisentraut 的锅,是他们在实现SQL/MED[4]时引入的。
create_foreignscan_path 等
create_foreignscan_path、create_foreign_join_path、create_foreign_upper_path 三个函数从PG 17开始都额外增加了一个参数 List *fdw_restrictinfo
。 从PG 12 开始[5], 将 create_foreignscan_path() 拆分成三个不同的函数,在此之前,postgres_fdw 使用 create_foreignscan_path() 来生成基础关系的路径,外部联接以及外部上层关系的路径。这是不正确的,因为 create_foreignscan_path() 调用了 get_baserel_parampathinfo(),该函数只对基础关系有效。
| 函数名 | 功能描述 | 使用场景 |
| create_foreignscan_path | 创建外部扫描路径,用于访问外部表数据。 | 查询外部数据源的基本扫描操作。 |
| create_foreign_upper_path | 创建外部上层路径,处理聚合或排序等操作。 | 处理上层的复杂查询需求。 |
| create_foreign_join_path | 创建外部连接路径,连接多个外部表。 | 需要从多个外部数据源中获取数据时。 |
duckdb_fdw_version
自动从duckdb.hpp
的三个宏计算得到
return (DUCKDB_MAJOR_VERSION * 10000) +
(DUCKDB_MINOR_VERSION * 100) +
std::stoi(DUCKDB_PATCH_VERSION);
🤔:是不是返回字符型的DUCKDB_VERSION
更好?或者再加上DUCKDB_SOURCE_ID
v1.1.2 f680b7d08f
简单速度测试
数据集
使用 Kaggle 上下载的 Transparency in Hospital Prices[6] 数据集,转为parquet格式了。
准备工作
create extension duckdb_fdw;
CREATE SERVER DuckDB_server FOREIGN DATA WRAPPER duckdb_fdw OPTIONS (database ':memory:');
SELECT duckdb_execute('duckdb_server','CREATE VIEW hospital_prices AS SELECT * FROM ''/Users/m2max/py/notebook/duckdb/hospital_prices.parquet''');
IMPORT FOREIGN SCHEMA public LIMIT TO(hospital_prices) FROM SERVER DuckDB_server INTO public;
create unlogged table hospital_prices_pg as
select * from hospital_prices limit 0;
-- 导入时间比较长,请耐心等待
COPY hospital_prices_pg
(cms_certification_num,
payer,
code,
internal_revenue_code,
units,
description,
inpatient_outpatient,
price,
code_disambiguator)
FROM '/Users/m2max/py/notebook/duckdb/hospital_prices.csv'
DELIMITER ','
CSV HEADER;
空间占用对比
| 格式 | 大小(GB) |
| csv | 31 |
| parquet | 1.4 |
| pg (unlogged) | 42 |


速度对比, duckdb_fdw vs PG unlogged table
postgres=# \timing on
Timing is on.
postgres=# select count(*) from hospital_prices;
count
-----------
296210747
(1 row)
Time: 90.963 ms
postgres=# select count(*) from hospital_prices_pg;
count
-----------
296210747
(1 row)
Time: 28250.129 ms (00:28.250)
postgres=# select 28250.129/90.963;
?column?
----------------------
310.5672526191968163
(1 row)
Time: 0.791 ms
postgres=# select code,count(*) from hospital_prices_pg group by 1 limit 3;
code | count
-------------+-------
+| 35
00406915076 |
+| 134
51754500105 |
#VALUE! | 8
(3 rows)
Time: 73079.285 ms (01:13.079)
postgres=# select code,count(*) from hospital_prices group by 1 limit 3;
code | count
-----------------+-------
HCPCS/CPT 81240 | 2002
HCPCS/CPT 81401 | 1240
HCPCS/CPT 81406 | 816
(3 rows)
Time: 587.465 ms
postgres=# select 73079.285/587.465;
?column?
----------------------
124.3976832662371375
(1 row)
Time: 0.271 ms

DuckDB下运行时间
D SELECT count(*) from 'hospital_prices.parquet' ;
┌──────────────┐
│ count_star() │
│ int64 │
├──────────────┤
│ 296210747 │
└──────────────┘
Run Time (s): real 0.043 user 0.073575 sys 0.014529
D SELECT code,count(*) from 'hospital_prices.parquet' GROUP BY 1 LIMIT 3;
┌─────────┬──────────────┐
│ code │ count_star() │
│ varchar │ int64 │
├─────────┼──────────────┤
│ 62322 │ 11236 │
│ 62368 │ 7206 │
│ 63057 │ 1239 │
└─────────┴──────────────┘
Run Time (s): real 0.616 user 4.821924 sys 0.332667

引用链接
[1]
dd04e958c8b03c0f0512497651678c7816af3198: https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=dd04e958c8b03c0f0512497651678c7816af3198[2]
PG 的百科Foreign_data_wrappers 页面: https://wiki.postgresql.org/wiki/Foreign_data_wrappers[3]
SQL/MED: https://wiki.postgresql.org/wiki/SQL/MED[4]
SQL/MED: https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=cae565e503c42a0942ca1771665243b4453c5770[5]
从PG 12 开始: https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=34ea1ab7fd305afe1124a6e73ada0ebae04b6ebb[6]
Transparency in Hospital Prices: https://www.kaggle.com/datasets/jpmiller/healthcare




