alitrack

DuckDB 2.0 有 11 个功能没进发布通告,我逐个测了

10 月 21 日 DuckDB 2.0 发布,这一周大家聊的都是 Quack 协议、VARIANT 类型、异步 I/O 这些大头。我在 duckdb-web 仓库里翻到了官方的下一篇博文:已经进了 PR(#7557),定档 10 月 29 日发布,标题叫 Gems of DuckDB 2.0.0。内容是官方团队自己盘点的、没写进发布通告的小功能。

11 项我全在本机 2.0.0 上跑了一遍:9 项开箱即用,最值的是 SECURE VIEW,它拿一套假统计换掉了视图里能看到的底表信息。

● ● ●

给业务方开的视图,一直在漏底表信息

先说最值得装的一个。

给业务方或者外部系统开查询权限,常用做法是建个视图把敏感行过滤掉。比如 all_users 表里有 alice(admin)和 bob(standard),视图只放行 standard 用户。看起来没问题,但在 2.0 之前,任何能查这个视图的人都可以执行:

SELECT stats(username) FROM v;

我实测的返回里,min 是 alice,max 是 bob。视图里明明只有 bob 一行,min 却把被过滤掉的 alice 招了出来。再配上 EXPLAIN,过滤条件 access_level = 'standard' 直接写在查询计划里。想猜被滤掉的行,min/max 加 approx_unique 基本够用。

2.0 新增的写法是 CREATE SECURE VIEW,同样的视图定义,行为变了。我拿普通视图和 SECURE VIEW 各跑了一遍对比:

普通视图照旧返回真统计。SECURE VIEW 的 EXPLAIN 里只剩一个 Secure View 节点,过滤条件看不见了。stats() 也不再返回真值——但这里有个官方博文没写对的细节:博文说它「returns NULL」,实测返回的不是 NULL,是一套固定假统计:has_no_null 是 True,has_null 也是 True。真数据里一行 null 都没有,这两个值自相矛盾。

挡是真挡住了,挡法比较野:靠返回矛盾值,让你没法从统计里反推任何东西。对使用者的建议就一条——升级 2.0 之后,把对外供数的视图换成 SECURE VIEW,别依赖「返回 NULL」这个说法,它实际返回的是一套假值。

普通视图与SECURE VIEW泄露对比

普通视图与SECURE VIEW泄露对比

● ● ●

三个马上能用的小改法

SHOW 能查配置了。 排障时想看 threads 设成多少,以前要写 SELECT current_setting('threads'),现在直接 SHOW threads,返回 42。我实测建了一张也叫 threads 的表,默认情况下 SHOW 优先描述表,用 SET show_behavior = 'setting' 可以强制查配置,PostgreSQL 老用户会觉得眼熟。

setval 能重置序列了。 批量导入完想把序列对齐到某个值,以前没辙,现在 SELECT setval('serial', 42) 之后 nextval 返回 43。传第三个参数 false 还能让下一次 nextval 直接返回 42 本身,两个语义我都实测过。

会话变量能当 prepared statement 的缺省参数。 这个写脚本模板的人会喜欢:

SET VARIABLE multiplier = 10;
PREPARE compute AS SELECT $multiplier * 42 AS result;
EXECUTE compute;   -- 420

EXECUTE 不带参数也能跑,缺的参数自动取同名会话变量。我改了 multiplier 为 2 再执行,结果从 420 变成 84——变量是执行时解析的,不是 prepare 时固化。

● ● ●

分区目录想怎么写就怎么写

写分区数据以前只有 Hive 风格:PARTITION_BY (year) 落盘就是 year=2019/、year=2020/ 一串目录。2.0 加了 PARTITION_PATH,目录名自己定。

我写了一批 2019 到 2022 的测试数据,用 CASE WHEN year < 2020 THEN 'archive' ELSE 'recent/' || year 分流,落盘结果:旧数据全进 archive/data_0.parquet,新数据进 recent/2021/、recent/2022/。冷热数据分目录,一条 COPY 搞定,不用导出后再挪文件。

配套还有个 ORDER_BY 选项,控制分区内落盘顺序。实测按 amount DESC 写入,读回文件的顺序就是 7.0、5.0、1.0。下游如果有按序读文件省内存的逻辑,这个能直接喂上。

PARTITION_PATH 目录结构对比

PARTITION_PATH 目录结构对比

● ● ●

GEOJSON 直接写出

spatial 扩展现在支持 COPY ... (FORMAT GEOJSON),不需要先写 Parquet 再用 Python 转。实测一个点落成标准 FeatureCollection,coordinates 是 [0.0, 1.0],属性列进了 properties。另有 GEOJSONL 格式,一行一个 Feature。做地图数据管道的少一步中转。

● ● ●

CLI 两处顺手的改动

.schema 默认变成多行缩进的美化输出,读表结构舒服多了。要复制建表语句原文的时候,加 --no-indent 回到单行,两种我都试了。

.history 能看带序号的命令历史。更有意思的是 shell_history() 表函数——历史记录变成了一张能用 SQL 查的表,我实测 WHERE sql ILIKE '%copy%' 直接筛出所有 COPY 语句。一个使用边界要说明:单命令模式(duckdb -c)下它返回空,历史要在交互会话里才加载。

● ● ●

Python 逐行写入有了新姿势

Python 侧新增了 appender:

with con.appender("people") as app:
    app.append_row(1, "Mark")
    app.append_rows([(2, "Hannes"), (3, "Gabor")])

with 块退出时统一 flush,实测三行落表。官方也说了适用边界:数据已经在 DataFrame 里的话,INSERT INTO ... SELECT 还是更快;appender 适合 Python 代码边生成边写的场景,比逐条 INSERT 少很多开销。

● ● ●

诚实边界

这篇博文还没正式发布,定档 10 月 29 日,最终措辞以正式版为准。我测的环境是 pip 的 duckdb 2.0.0 加官方 CLI v2.0.0(d54cbbe3df),全部结论来自本机实测输出,没转述博文里没验证过的数字。

上周写 2.0 升级注意事项的时候,重点全在存储格式这道门槛上:新版本建的文件旧版本打不开。测完这批小功能,我现在的看法是,门槛是真实的,门槛后面这些东西也是真实的。如果你的库要对外供数,SECURE VIEW 一个就值回升级的折腾。

这 11 项里你最想用哪个:SECURE VIEW、PARTITION_PATH,还是 shell_history()?评论区聊聊,我看到会回。

参考来源

  1. 01
    https://github.com/duckdb/duckdb-web/pull/7557(博文 PR,定档 2026-10-29,未发布)
  2. 02
    https://github.com/duckdb/duckdb/releases/tag/v2.0.0
  3. 03
    https://duckdb.org/2026/10/21/announcing-duckdb-200
  4. 04
    https://duckdb.org/docs/2.0/whats_new_in_the_docs.html

本机实测环境:Python duckdb 2.0.0(pip)+ 官方 CLI v2.0.0 "Cyanoptera"(d54cbbe3df),WSL。文中全部 SQL 输出为实际运行结果。