SQLite版本更新录(202x)
2022-11-21 13:30:28 阿炯

本文是从SQLite的产品主页分离出来的,专门用于该发行版本的更新记录,截止到2030年之前。SQLite 是一个 C 语言库,实现了一个小型、快速、独立、高可靠性、全功能的 SQL 数据库引擎。它是世界上使用最多的数据库引擎,其源代码属于公共领域,每个人都可以免费使用,用于任何目的。

最新版本:3.12
该版本最值得关注的就是增加对内存映射 I/O 的支持,该特性可潜在提升两倍的性能,同时降低内存的使用。增加新的函数 xFetch() 和 xUnfetch() 可在内存映射 I/O 激活时自动被调用,用于映射数据到内存。由于一些开发者认为该技术有一些不足,在某些测试案例下性能并没有变化,因此该特性默认关闭。主要的性能提升是通过重构 B-Tree 重整逻辑来避免不必要的 memcpy() 操作。新特性包括 PRAGMA data_version 状态,可以访问一个 VALUES clause,不限制任意行数。此外还解决了一些 Mac 上的编译错误和多线程问题。修复一个 3.12.0 和 3.12.1 中的回归兼容问题:列声名为 "INTEGER" 主键(用引号括起数据类型关键字)不会被识别为 整形主键,导致数据库文件不兼容。
修复一个 PRAGMA reverse_unordered_selects 打开时导致 DELETE 操作丢失行的问题(从 3.9.0 开始出现)。
修复一个导致代码生成器结果不正确问题,如果两个或多个 虚表 连接并且在 IN 操作 约束连接的外循环中使用虚表。
修正当使用 cache size 排序大量数据时“PRAGMA cache_size”值无效的问题。

在此将介绍 SQLite 在 2018 年新增加的 SQL 功能,也就是从版本 3.22 到 3.26 所新增加的 SQL 功能,具体内容包括:
布尔字面量和判断
窗口函数
Filter子句
Insert … on conflict ("Upsert")
重命名列

布尔变量和判断

SQLite支持“假”布尔值:它接受Boolean作为类型的名称,但它将其当作整数看待(这一点非常类似于MySQL)。真值true和false分别由数值1和0表示(这一点和C语言一样)。从版本3.23.0开始,SQLite将关键字true和false分别用数字1和0表示,并支持is [not] true | false的判断语句。现在,它不再支持关键字unknown。开发者可以使用空值null来代替,因为unknown和null的布尔值是一样的。在INSERT和UPDATE语句中,字面量true和false可以大大提高values和set子句的可读性。

is [not] true | false这个判断语句很有用,它与比较操作的含义不一样:
来比较一下
WHERE c <> FALSE
WHERE c IS NOT FALSE

在上面的例子中,如果c是null, 那么c <> false的结果是unknown。

这是因为WHERE子句只接受结果为true的值,它会过滤掉结果为false或unknown的值。这样,它就会把对应的行从结果中去掉。

与此相对应,如果c是null,那么c is not false的判断结果是true。因此第二个WHERE子句也将包含c是null的行。

要达到同样的效果,可以采用的另外一种方法是增加单独处理null值的子句。也就是使用语句:
WHERE c <> FALSE OR c IS NULL

这种形式的语句更长并且有一些冗余语句(c被使用了两次)。长话短说,可以使用is not false判断来替代这个or…is-null的语句。更详细的内容,请参考“Binary Decisions Based on Three-Valued Results”。

SQLite中对布尔字面量和布尔判断的支持现在和其他开源数据库接近,唯一的差距是SQLite不支持is[not] unknown(你可以使用is [not] null来代替)。有趣的是,这些功能在下面提到的商用产品中还不可用。


0:只支持true,false.不支持notknown,如果需要,用null代替
1:不支持is [not] unknown,如果需要,用is [not] null代替

窗口函数

3.25.0引入了窗口函数。如果你知道窗口函数,那么也知道这是一件大事。如果你不了解窗口功能,请你自己学习如何使用。这篇文章不会具体解释窗口函数,但请相信:它是最重要的“现代”SQL特性。

SQLite对over子句的支持与其他数据库非常接近。唯一值得注意的限制是range语句不支持数字或间隔距离(仅支持current row和unbounded preceding|following)。在发布sqlite 3.25.0时,SQL Server和PostgreSQL具有同样的限制。PostgreSQL 11消除了这一限制。


0:没有变化

1:Range范围定义不支持datetime类型

2:Range范围不接受关键字 (只支持unbounded和current row)

SQLite对于窗口函数的支持在业界是领先的。它不支持的功能在其他一些主要产品中也同样不支持(在聚合中语句中的distinct,width_bucket, respect|ignore nulls和from first|last等语句)。


0:同样没有ORDER BY 语句

1:不允许负偏移量,nulls的特定处理:lead(, 'IGNORE NULLS'),这里是字符串参数

2:没有缺省值(第三个参数),不支持respect|ignore nulls语句

3:不允许负偏移量,不支持ignore nulls语句

4:不允许负偏移量

5:不支持respect|ignore nulls语句

6:不允许负偏移量,不支持respect|ignore nulls语句

7:nulls的特定处理:first_value(, 1, null, 'IGNORE NULLS') ,这里是字符串参数。

8:不支持ignore nulls语句

9:不支持ignore nulls语句和from last语句

过滤语句

虽然filter语句只是语法糖——你也可以很容易地使用表达式来获得相同的结果——我认为它也是必不可少的语法糖,因为它能使人们更加容易地学习和理解SQL语句。看看如下的select子句,觉得哪一个更容易理解?

SELECT SUM(revenue) total_revenue , SUM(CASE WHEN product = 1 THEN revenue END ) prod1_revenue ...

SELECT SUM(revenue) total_revenue , SUM(revenue) FILTER(WHERE product = 1) prod1_revenue ...

此示例很好地总结了filter子句的作用:它是聚合函数的后缀,可以在进行聚合之前根据特定条件,过滤掉相应的行。pivot技术是filter子句最常见的用例。这包括将实体属性值(EAV)模型中的属性转换为表格的列,如果想了解更多的内容,可以参考链接“filter-Selective Aggregates”。

SQLite 从版本3.25.0开始,在使用over子句的聚合函数中支持了filter子句,但是在使用group by子句的聚合函数中还不支持。不幸的是,这意味着您仍然无法在SQLite中使用filter语句来处理上述情况。你必须像以前一样使用case表达式。我真的希望SQLite在这一点上能尽快做到。


Insert … on conflict (“Upsert”)

SQLite 从版本3.24.0开始,引入了“upsert”概念:它是一个insert语句,可以优雅地处理主键和唯一约束的冲突。您可以选择忽略这些冲突(在on conflict语句中什么都不做)或者更新当前行(在on conflict语句中执行更新操作)。

这是一个特有的SQL扩展,即它不是标准SQL的一部分,因此在下面的矩阵中是灰色的。但是,SQLite遵守与PostgreSQL相同的语法来实现此功能0。该标准提供了对merge语句的支持。与PostgreSQL不同,SQLite在以下语句中存在问题。

INSERT INTO target SELECT * FROM source ON CONFLICT (id) DO UPDATE SET val = excluded.val

根据说明文档,这是因为解析器无法判断关键字ON是SELECT语句的连接约束还是upsert子句的开头。你可以通过向查询中添加子句来解决,例如where true。

INSERT INTO target SELECT * FROM source WHERE true ON CONFLICT (id) DO UPDATE SET val = excluded.val


0:同样记录insert、update、delete和merge操作的错误信息 (“DML error logging”)

1:On conflict语句不能紧挨查询的from语句,如果需要,可以添加 where true语句来分隔。

重命名列

SQLite引入的另一个特有功能是重命名基准数据库表中的列1。标准的SQL不支持此类功能2。其遵循其他产品常用的语法来重命名列:
ALTER TABLE … RENAME COLUMN … TO


0:请查阅 sp_rename.

其他消息

在2018年,SQLite除了在SQL语法上的变化,还有一些应用程序接口(API)的变化。可以查阅sqlite官网上的新闻部分来了解更详细的消息。

脚标:
0:SQLite通常遵循PostgreSQL语法,Richard Hipp将此称为PostgreSQL会怎么做(WWPD)。
1:基准数据库表是指用Create table语句创建的数据库表。派生的数据库表(如Select语句返回的查询结果集)中的列名可以通过SELECT语句、FROM语句或WITH语句来进行改变
2:据我所知,也许可以通过可更新视图或派生的列来模拟该功能。


最新版本:3.28
此版本更新内容包括:
新增 sqlite3_stmt_isexplain(S) 接口,用于确定预准备语句是否为 EXPLAIN。
增强 VACUUM INTO,用于只读数据库,一些查询优化。
增强 window function、增强 TCL 接口、CLI 增强。
添加 fossildelta.c 扩展,可以创建、应用和解析 RBU 扩展使用的 Fossil DVCS 文件增量格式。
为 sqlite3_db_config() 接口添加了 SQLITE_DBCONFIG_WRITABLE_SCHEMA 谓词,它在不使用 SQL 解析器的情况下执行与 PRAGMA writable_schema 相同的工作。
新增 sqlite3_value_frombind() API,用于确定 SQL 函数的参数是否来自绑定参数。
fts3_tokenizer() 安全性与兼容性增强。
提高了健壮性,防止数据库文件损坏。
其它性能增强。
详情查看更新说明

最新版本:3.36
SQLite 3.36.0 于2021年6月中旬发布了,此版本更新内容包括:
对 EXPLAIN QUERY PLAN 输出的改进,使其更容易理解。
token 开始的字节顺序标记被跳过,就像它们是空白的一样。
在试图访问一个 VIEW 或子查询的 rowid 时,会产生一个错误。以前一个 VIEW 的 rowid 是不确定的,而且经常是 NULL。-DSQLITE_ALLOW_ROWID_IN_VIEW 编译时选项可用于恢复需要它的应用程序的传统行为。
sqlite3_deserialize() 和 sqlite3_serialize() 接口现在被默认启用。不再需要 -DSQLITE_ENABLE_DESERIALIZE 编译时选项了。取而代之的是一个新的 -DSQLITE_OMIT_DESERIALIZE 编译时选项来省略这些接口。
"memdb"VFS 现在允许同一进程中的多个数据库连接共享同一个内存数据库,只要数据库名称以"/"开头。
取消了 EXISTS-to-IN 优化(SQLite 3.35.0 变更日志中的第 8b 项),因为发现它降低了查询速度,而不是加快了查询速度。
改进 constant-propagation 优化,使其适用于非连接查询。
REGEXP 扩展现在包含在 CLI 构建中。

最新版本:3.38
3.38.0 现已于2022年2月下旬发布。主要变化如下:
添加了 -> 和 ->> 运算符以便于处理 JSON。新的运算符与 MySQL 和 PostgreSQL 兼容。JSON 函数现在是内置的。不再需要使用 -DSQLITE_ENABLE_JSON1 编译时选项来启用 JSON 支持。默认情况下启用 JSON。使用新的 -DSQLITE_OMIT_JSON 编译时选项禁用 JSON 接口。
日期和时间功能的增强:
添加了 unixepoch() 函数。
添加了 auto 修饰符和 julianday 修饰符。

将 printf() SQL 函数重命名为 format() 以获得更好的兼容性。保留原始 printf() 名称作为别名以实现向后兼容性。
添加了 sqlite3_error_offset() 接口,有时可以帮助将 SQL error 定位到输入 SQL 文本中的特定字符,以便应用程序可以提供更好的错误消息。
增强了 virtual tables 的接口如下:
添加了 sqlite3_vtab_distinct() 接口。
添加了 sqlite3_vtab_rhs_value() 接口。
添加了新的 operator 类型 SQLITE_INDEX_CONSTRAINT_LIMIT 和 SQLITE_INDEX_CONSTRAINT_OFFSET。
添加了 sqlite3_vtab_in() 接口(及相关接口)以使 virtual table 能够一次处理所有 IN operator 约束,而不是单独处理 IN operator 右侧的每个值。

CLI enhancement:
Columnar output modes 得到增强,可以正确处理嵌入在文本中的制表符和换行符。
在 Columnar output modes 中添加了“--wrap N”、“--wordwrap on”和“--quote”等选项。
添加了 .mode qbox 别名。
.import 命令自动消除列名的歧义。
使用新的 sqlite3_error_offset() 接口提供更好的错误消息。

Query planner enhancements:
使用 Bloom filter 来加速大型分析查询。
使用 balanced merge tree 来评估具有 ORDER BY 子句的 UNION 或 UNION ALL 复合 SELECT 语句。

ALTER TABLE 语句更改为静默忽略 sqlite_schema 表中在时不解析的条目。改变了 ALTER TABLE 语句,当 PRAGMA writable_schema=ON 时,静默忽略 sqlite_schema table 中没有解析的条目。

最新版本:3.40
SQLite 3.40 于2022年11月中旬正式发布,更新内容如下:
增加对将 SQLite 编译为 WASM 和在网络浏览器中运行的支持
增加恢复扩展,可能能够从损坏的数据库文件中恢复一些内容
查询规划器的增强
增加了一个名为 sqlite3_filename 的新类型定义,用来表示数据库文件的名称。
增加了 sqlite3_value_encoding() 接口。
安全性增强:增强了 SQLITE_DBCONFIG_DEFENSIVE,以禁止改变 schema_version
增强了 PRAGMA integrity_check 语句
增强 VACUUM INTO 语句,使其遵守 PRAGMA 的同步设置
增强了 sqlite3_strglob() 和 sqlite3_strlike() API,使它们能够为字符串参数接受 NULL 指针,并且仍然产生一个合理的结果。
提供新的 SQLITE_MAX_ALLOCATION_SIZE 编译时选项来限制内存分配的大小。
将 SQLite 内置的伪随机数生成器(PRNG)使用的算法从 RC4 改为 Chacha20。
允许两个或多个索引具有相同的名称,只要它们都在不同的模式中
其他性能优化使典型工作负载中使用的 CPU 周期减少约 1%

更多详情可查看此处

3.42.0 正式于2023年5月中旬发布,更新内容如下:
增加 FTS5 secure-delete 命令。当内容被删除时,这个选项会使所有取证分析从 FTS5 的倒置索引中被删除。
增强 JSON SQL 函数以支持 JSON5 扩展。
现在允许对 sqlite3_config () 的 SQLITE_CONFIG_LOG 和 SQLITE_CONFIG_PCACHE_HDRSZ 调用发生在 sqlite3_initialize () 之后。
新增了 sqlite3_db_config () 选项: SQLITE_DBCONFIG_STMT_SCANSTATUS 和 SQLITE_DBCONFIG_REVERSE_SCANORDER。

查询计划器的改进:
默认启用 "count-of-view" 优化。
避免在子查询中计算未使用的列。

对下推优化的改进
对 CLI 的改进:
增加 --unsafe-testing 命令行选项。如果没有这个选项,一些点状命令(例如:".testctrl")现在被禁用,因为这些命令只用于测试,可能会导致故障误用。
允许命令 .log on 和 .log off,即使在 --safe 模式下。
-- 作为一个命令行参数意味着所有以 - 开头的后续参数都被解释为普通的非选项参数。
Magic 参数 :inf 和 :nan 分别与浮点字元 Infinity 和 NaN 绑定。
--utf8 命令行选项在交互式会话中省略了所有与 Windows 控制台的 MBCS 的转换,并在这种会话中为 UTF-8 I/O 设置控制台代码页。--utf8 选项在所有其他平台上都是无用的。
增加应用程序定义的 SQL 函数与连接关键字同名的能力:CROSS, FULL, INNER, LEFT, NATURAL, OUTER, 或 RIGHT。

增强 PRAGMA integrity_check 的功能:
当 NaN 值被存储在 NOT NULL 列中时,检测并引发一个错误。
改进了错误信息输出,当在 b-tree 中发现错误时,可以识别 b-tree 的根页面。
允许对会话扩展进行配置,以捕获缺乏明确 ROWID 的表的变化。
为日期和时间函数添加了 subsecond 修改器。
传入 sqlite3_sleep () 的负值从此被解释为 0。
JSON 数组和对象的最大递归深度从 2000 降低到 1000。
扩展了内置的 printf () 函数,因此除了整数转换外,逗号选项现在也可以用于浮点转换。

更多详情可查看此处

用 Rust 编写的 SQLite 压缩扩展 sqlite-zstd

为 SQLite 提供透明的基于字典的行级压缩,于2022年11月发布。将允许压缩 SQLite 数据库中的条目,就像压缩整个数据库文件一样,但同时保留了随机访问。根据不同的数据,这可以将数据库的大小减少 80%,同时保持大部分的性能不变(甚至可以提高性能,因为从磁盘上读取的数据更小)。该项目可以以两种模式构建:作为 Rust 库;作为纯 SQLite 扩展。


可以从 GitHub Release 中获取 SQLite 扩展二进制文件,或者可以手动构建扩展:
cargo build --release --features build_extension
# should give you target/release/libsqlite_zstd.so

可以将此库作为 SQLite 扩展或作为 Rust 库加载。请注意 sqlite 扩展不是持久的,因此每次连接数据库时都需要加载它。

Sqlite CLI
在 REPL 中加载它:
$ sqlite3 file.db
SQLite version 3.34.0 2020-12-01 16:14:00
sqlite> .load .../libsqlite_zstd.so
[2020-12-23T21:30:02Z INFO  sqlite_zstd::create_extension] [sqlite-zstd] initialized
sqlite>
或者:
sqlite3 -cmd '.load libsqlite_zstd.so' 'select * from foo'

C API
int success = sqlite3_load_extension(db, "libsqlite_zstd.so", NULL, NULL);

Rust
推荐的方法是添加 sqlite_zstd 作为项目的依赖项,然后使用
let conn: rusqlite::Connection;
sqlite_zstd::load(&conn)?;

或者也可以像任何其他扩展一样加载扩展:
let conn: rusqlite::Connection;
conn.load_extension("libsqlite_zstd.so", None)?;

最新版本:3.43
v3.43 已于2023年8月下旬正式发布。主要变化
添加对 Contentless-Delete FTS5 索引的支持。这是 FTS5 全文搜索索引的变体,它省略了存储正在索引的内容,同时支持删除记录
- Contentless-Delete 表支持 DELETE 和 "INSERT OR REPLACE INTO" 语句
- Contentless-Delete 表支持 UPDATE 语句,但前提是为 fts5 表的所有用户定义列提供了新的值
- Contentless-Delete 表不支持 FTS5 删除命令

增强日期和时间函数:
添加了 ±YYYY-MM-DD HH:MM:SS.SSS 形式的新版时间位移修饰符 (time shift modifiers)
新增 timediff() SQL 函数
新增 octet_length(X) SQL 函数
新增 sqlite3_stmt_explain() API
增强 JSON 处理性能,大型 JSON 字符串某些类型处理的性能提升了 2 倍
增强 Query planner 性能
更多详情查看发行公告

最新版本:3.44
3.44.0 现已于2023年10月下旬发布,更新内容如下:
聚合函数现在可以在其最后一个参数后包含 ORDER BY 子句。函数的参数按照指定的顺序进行处理。这对于 string_agg () 和 json_group_array() 等函数非常重要 。
添加对 concat () 和 concat_ws() 标量 SQL 函数的支持,与 PostgreSQL、SQLServer 和 MySQL 兼容。
添加对 string_agg () 聚合 SQL 函数的支持,兼容 PostgreSQL 和 SQLServer。
strftime () SQL 函数上的新转换字母:% e % F % I % k % l % p % P % R % T % u
添加新的 C 语言 API: sqlite3_get_clientdata() 和 sqlite3_set_clientdata()。
现在,当运行 CREATE TABLE 语句本身时,会引发许多与 CREATE TABLE 相关的错误,而不是推迟到第一次实际使用表时。
PRAGMA integrity_check 命令现在使用新的 xIntegrity 方法验证各种内置虚拟表中内容的一致性。这适用于 FTS3、FTS4、FTS5、RTREE 和 GEOPOLY 扩展。
SQLITE_DBCONFIG_DEFENSIVE 设置现在可防止打开 PRAGMA writable_schema 。以前可以打开 writable_schema,但实际上不允许模式可写。现在则根本无法开启。
将内置 FTS3、FTS4、FTS5、RTREE 和 GEOPOLY 虚拟表标记为 SQLITE_VTAB_INNOCUOUS,以便它们可以在高安全性部署中的触发器内部使用。
PRAGMA case_sensitive_like 语句已弃用,因为当模式包含 LIKE 运算符时使用它可能会导致 PRAGMAintegrity_check 报告数据库损坏。
现在,只要使用 Microsoft C 编译器构建 SQLite,就会默认启用 SQLITE_USE_SEH (结构化异常处理)。可以使用 -DSQLITE_USE_SEH=0 禁用它
Query planner 优化:
    在部分索引扫描中,如果 WHERE 子句暗示表列的常量值,则用该常量替换该表列的出现。这增加了部分索引作为覆盖索引的可能性。
    禁用视图扫描优化(在版本 3.42.0 中添加 - item 1c),因为它会导致多个性能下降。取而代之的是,将 DISTINCT 子查询的估计行数减少 8 倍。
SQLite 现在执行运行时检测,检测底层硬件是否支持精度大于 “double” 的 “long double”,并根据检测结果使用适当的浮点例程。
Windows CLI 现在默认在支持 UTF-8 的平台上使用 UTF-8 进行输入和输出。--no-utf8 选项可用于禁用 UTF8 支持。

重写 JSON 函数并推出 JSONB

SQLite 于2023年12月上旬宣布推出 JSONB,这是对 SQLite JSON 函数的重写,宣称比现有 JSON 函数 “快几倍”。在过去几年中,SQLite 开发者为改进 JSON 数据与该 SQL 数据库的库集成做了大量工作,最新的工作是已合并的 JSONB。JSONB 对 SQLite JSON 函数进行了重写,将 JSON 的内部使用从二进制表示形式更改改为连续的字节数组,可以作为 SQL BLOB 读取或写入。JSONB 有助于降低解析 JSON 文本和表示用于存储时的开销。此外 JSONB 保留所有旧功能,并且所有 JSON 函数也可以接受 JSONB 二进制内容。如需了解有关 SQLite JSONB 功能及其更快性能潜力的更多信息可查看此处

SQLite 3.45 已于2024年1月下旬发布。新版本重写了所有 JSON 函数,改为新的内部解析树格式 JSONB。该JSON格式可以序列化并存储在数据库中,避免在使用 JSON 值时,还需要进行不必要的重复解析。因此通过支持 JSONB,SQLite 在处理 JSON 数据时能够更高效灵活。此外,JSONB 保留所有旧功能,并且所有 JSON 函数也可以接受 JSONB 二进制内容。官方承诺其生成的 JSONB 格式良好,但是由于 JSONB 本质上就是一个 BLOB,因此可能存在格式不正确的 JSONB,而 SQLite 在处理格式不正确的 JSONB,就可能出现不同行为,从而发生错误。不过 SQLite 也保证格式错误的 JSONB 永远不会导致内存错误或是类似问题,也不会出现系统漏洞。3.45.3 现已于2024年4月中旬发布,更新内容如下:
修复一个长期存在的错误(可追溯到版本 3.24.0),如果 UPDATE 触发器触发 UPSERT,该错误可能(很少)导致该触发器的 "old.*" 值不正确。
修复 sum() 中的一个错误,该错误可能导致它在应该返回 Infinity 时返回 NULL。
自上一个补丁发布以来出现的其他琐碎更正和编译器警告修复。