暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

duckdb_fdw 支持Postgres 17 与 DuckDB 1.1.2了

alitrack 2024-11-04
222

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
...


从 PG 的百科Foreign_data_wrappers 页面[2] 得知,PG第一个fdw的出现时间是2011年, 在 2003 年,SQL 标准中新增了一项名为 SQL/MED[3] (“SQL 外部数据管理”)的规范。这为 SQL 数据库访问远程对象提供了一种标准化的方法。2011 年,PostgreSQL 9.1 发布,支持该标准的只读功能,而在 2013 年,PostgreSQL 9.3 则增加了对写入的支持。


原来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)
csv31
parquet1.4
pg (unlogged)42

hostpital_prices 在PG上的空间占用

hostpital_prices各种格式下文件大小

速度对比, duckdb_fdw vs PG unlogged table

postgres=# \timing on
Timing is on.
postgres=select count(*from hospital_prices;
   count
-----------
 296210747
(1 row)

Time90.963 ms
postgres=select count(*from hospital_prices_pg;
   count
-----------
 296210747
(1 row)

Time28250.129 ms (00:28.250)
postgres=select 28250.129/90.963;
       ?column?
----------------------
 310.5672526191968163
(1 row)

Time0.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)

Time73079.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)

Time587.465 ms
postgres=select 73079.285/587.465;
       ?column?
----------------------
 124.3976832662371375
(1 row)

Time0.271 ms

duckdb_fdw vs pg unlogged table

DuckDB下运行时间

SELECT count(*from 'hospital_prices.parquet' ;
┌──────────────┐
│ count_star() │
│    int64     │
├──────────────┤
│    296210747 │
└──────────────┘
Run Time (s): real 0.043 user 0.073575 sys 0.014529
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

duckdb 

引用链接

[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


文章转载自alitrack,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论