发布 DuckDB 1.2.0

DuckDB团队
2025-02-05 · 8 分钟阅读

简而言之:DuckDB 团队很高兴地宣布,我们今天发布了 DuckDB 1.2.0 版本,代号为“Histrionicus”。

要安装新版本,请访问安装指南。有关发布说明,请参阅发布页面

由于发布流程中需要审核,一些软件包(Go、R、Java)的发布可能需要额外几天时间。

我们很荣幸发布 DuckDB 1.2.0。此版本代号为“Histrionicus”,取自外形优美的斑脸海番鸭 (Histrionicus histrionicus),它们栖息在“北美、格陵兰岛、冰岛和俄罗斯东部寒冷湍急的溪流中”。

1.2.0 版本新特性

改动内容太多,无法一一详细讨论,但我们想重点介绍一些特别重要和令人兴奋的特性!下面是这些新特性及其示例的总结。

不兼容的更改

random 函数现在使用更大的状态。 这意味着它现在更随机™。由于此更改,固定的种子现在将生成与 DuckDB 以前版本不同的值。

map['entry'] 现在返回一个值,而不是一个条目列表 例如,map(['k'], ['v'])['k'] 现在返回 'v',而以前返回 ['v']。我们还引入了 map_extract_value 函数,它现在是方括号运算符 [] 的别名。如果您想返回一个列表,请使用map_extract 函数map_extract(map(['k'], ['v']), 'k') = ['v']

list_reduce 的索引已修复。 当索引应用于 list_reduce 时,索引指向 lambda 函数的最后一个参数,并且索引从 1 开始。因此,list_reduce(['a', 'b'], (x, y, i) -> x || y || i) 返回 ab2

显式存储版本

DuckDB v1.2.0 包含了新的压缩方法,但默认尚未启用,以确保旧版本的 DuckDB 能够读取由 DuckDB v1.2.0 生成的文件。

实际上,这意味着 DuckDB v1.2.0 可以读取由过去的稳定 DuckDB 版本(如 v1.0.0)写入的数据库文件。在使用默认设置的 DuckDB v1.2.0 时,旧版本可以读取由 DuckDB v1.2.0 写入的文件。

您可以使用以下语法选择启用新的向前不兼容特性

ATTACH 'file.db' (STORAGE_VERSION 'v1.2.0');

此设置指定了能够读取数据库文件的最小 DuckDB 版本。当数据库文件使用此选项写入时,生成的文件无法被早于指定版本的 DuckDB 发布版本打开。它们可以被指定版本和所有更新的 DuckDB 版本读取。

如果您连接到 DuckDB 数据库,可以使用以下命令查询存储版本

SELECT database_name, tags FROM duckdb_databases();

这显示了存储版本

┌───────────────┬───────────────────────────────────┐
│ database_name │               tags                │
│    varchar    │       map(varchar, varchar)       │
├───────────────┼───────────────────────────────────┤
│ file1         │ {storage_version=v1.2.0}          │
│ file2         │ {storage_version=v1.0.0 - v1.1.3} │
│ ...           │ ...                               │
└───────────────┴───────────────────────────────────┘

这意味着 file2 可以被过去的 DuckDB 版本打开,而 file1 仅与 v1.2.0(或未来版本)兼容。

要在 DuckDB v1.2.0 中将新格式转换为旧格式以实现兼容性,请使用以下序列

ATTACH 'file1.db';
ATTACH 'converted_file.db' (STORAGE_VERSION 'v1.0.0');
COPY FROM DATABASE file1 TO converted_file;

索引

ALTER TABLE ... ADD PRIMARY KEY 经过很长一段时间,DuckDB 终于能够为现有表添加主键了 🎉。因此,现在可以运行此操作

CREATE TABLE tbl (id INTEGER);
INSERT INTO tbl VALUES (42);
ALTER TABLE tbl ADD PRIMARY KEY (id);

解决了过度活跃的约束检查问题。 我们还解决了过度活跃的唯一约束检查这一长期存在的问题。例如,以下命令序列过去会抛出错误,但现在可以正常工作了

CREATE TABLE students (id INTEGER PRIMARY KEY, name VARCHAR);
INSERT INTO students VALUES (1, 'John Doe');

BEGIN; -- start transaction
DELETE FROM students WHERE id = 1;
INSERT INTO students VALUES (1, 'Jane Doe');

CSV 特性

Latin-1 和 UTF-16 编码。 以前,DuckDB 的 CSV 读取器仅限于 UTF-8 文件。它现在可以读取 Latin-1 和 UTF-16 文件。例如

FROM read_csv('cities-latin-1.csv', encoding = 'latin-1');

多字节分隔符。 DuckDB 现在支持最长 4 字节的分隔符。这意味着您终于可以将鸭子表情符号用作列分隔符了。例如

a🦆b
hello🦆world
FROM read_csv('example.dsv', sep = '🦆');

严格 CSV 解析。 RFC 4180 规范定义了良好格式的 CSV 文件的要求,例如,具有单一的行分隔符。默认情况下,DuckDB 现在以所谓的严格模式(`strict_mode = true`)解析 CSV。例如,以下 CSV 文件由于混合了换行符而被拒绝

echo "a,b\r\nhello,42\nworld,84" > rfc_4180-defiant.csv
FROM read_csv('rfc_4180-defiant.csv');
Invalid Input Error:
Error when sniffing file "rfc_4180-defiant.csv".
It was not possible to automatically detect the CSV Parsing dialect/types

但它可以与更宽松的选项 strict_mode = false 一起解析

FROM read_csv('rfc_4180-defiant.csv', strict_mode = false);
┌─────────┬───────┐
│    a    │   b   │
│ varchar │ int64 │
├─────────┼───────┤
│ hello   │    42 │
│ world   │    84 │
└─────────┴───────┘

性能改进。 新版本中的 CSV 解析器使用了一种新的算法在并行执行时查找新行。这使得速度提高了约 15%。

无限行长度。 以前,DuckDB 的 CSV 文件行长限制为 8 MB。新版本取消了这一限制,行长可以任意长。

Parquet 特性

Parquet 字典和布隆过滤器支持。 DuckDB 现在支持使用字典编码写入更多类型。这在某些情况下可以减小文件大小。DuckDB 现在还能够读取和写入 Parquet 布隆过滤器。布隆过滤器是小型索引数据结构,如果设置了过滤器,可用于排除行组。这对于经常重复但无序的数据(例如,分类值)特别有用。后续将发布一篇单独的博客文章。

Parquet 的 Delta 二进制打包压缩。 DuckDB 现在支持 DELTA_BINARY_PACKED 压缩以及 Parquet 文件的 DELTA_LENGTH_BYTE_ARRAYBYTE_STREAM_SPLIT 选项。几周前,我们在一篇博客文章中详细阐述了这些内容。

CLI 改进

安全模式。 DuckDB 命令行客户端现在支持安全模式,可以通过 -safe 标志或 .safe_mode 点命令激活。在此模式下,CLI 客户端被阻止访问除其最初连接的数据库文件之外的外部文件,并被阻止与主机文件系统交互。有关更多信息,请参阅操作手册中的“保护 DuckDB”页面

更好的自动补全。 CLI 中的自动补全现在使用解析表达式文法 (PEG) 以提供更好的自动补全,以及改进的错误消息和建议。

美观地打印大数字。 如果客户端只渲染单行,CLI 会提供打印数字的摘要。

SELECT 100_000_000 AS x, pi() * 1e9 AS y;
┌──────────────────┬───────────────────┐
│        x         │         y         │
│      int32       │      double       │
├──────────────────┼───────────────────┤
│    100000000     │ 3141592653.589793 │
│ (100.00 million) │  (3.14 billion)   │
└──────────────────┴───────────────────┘

友好的 SQL

前缀别名。 SQL 表达式和表别名现在可以指定在它们所指代的事物之前(而不是使用众所周知的 AS 语法)。这在某些情况下可以提高可读性,例如

SELECT 
    e1: some_long_and_winding_expression,
    e2: t2.a_column_name 
FROM
    t1: long_schema.some_long_table_name,
    t2: short_s.tbl;

此想法归功于Michael Toy。一篇单独的博客文章将很快发布。更新:关于前缀别名的博客文章已发布。

RENAME 子句。 DuckDB 现在支持在 SELECT 中使用 RENAME 子句。这允许重命名由* 表达式发出的字段

CREATE TABLE integers (col1 INTEGER, col2 INTEGER);
INSERT INTO integers VALUES (42, 84);
SELECT * RENAME (col1 AS new_col1) FROM integers;

星号 LIKE LIKESIMILAR TO 子句现在可以用于 * 表达式,作为 COLUMNS 语法的简写。

CREATE TABLE key_val (key VARCHAR, val1 INTEGER, val2 INTEGER);
INSERT INTO key_val VALUES ('v', 42, 84);
SELECT * LIKE 'val%' FROM key_val;
┌───────┬───────┐
│ val1  │ val2  │
│ int32 │ int32 │
├───────┼───────┤
│  42   │  84   │
└───────┴───────┘

优化

我们花费了大量时间在 DuckDB 的优化器上。优化器的改进难以量化,但由于这些优化,例如,在 MacBook Pro 上运行 TPC-H SF100 查询时,DuckDB 的总运行时间比以前的版本提高了 13%

扩展的 C API

目前,DuckDB 扩展使用 DuckDB 的内部 C++ 结构。这——加上一些有趣的链接问题——要求扩展与主线 DuckDB 同步开发并不断更新。从这个版本开始,我们在 duckdb_extension.h 中公开了一个新的 C 风格扩展 API。此 API 可用于在 DuckDB 中创建标量、聚合或表函数。使用此 API 有两个主要优点:首先,许多编程语言(例如 Go、Rust 甚至 Java)都直接绑定到 C API,使得集成相当容易。其次,C 扩展 API 是稳定的且向后兼容的,这意味着针对此 API 的扩展将继续适用于新的 DuckDB 版本。我们将跟进提供一个新的扩展模板。

musl 扩展

为 musl 分发扩展。 musl C 库常用于轻量级设置,例如运行 Alpine Linux 的 Docker 环境。从这个版本开始,我们正式支持 musl,并为 linux_amd64_musl 平台分发扩展(但尚未支持 linux_arm64_musl)。请注意,DuckDB 二进制文件(例如 CLI 客户端)尚未针对 musl 平台分发,因此您必须从源代码构建它们

结语

以上只是一部分亮点——此版本还有更多特性和改进。自 1.1.3 版本发布以来,已有超过 70 位贡献者提交了超过 5,000 次提交。完整(非常长)的发布说明可以在 GitHub 上找到

我们再次感谢我们出色的社区使用 DuckDB,在 DuckDB 上构建酷炫项目,并通过向我们提供反馈来改进 DuckDB。您的贡献意义重大!