无效的 UTF 政策

1.垃圾进,垃圾出

对于无效的 UTF,SQLite 遵循 Garbage-In, Garbage-Out (GIGO) 策略。如果您将无效的 UTF 插入 SQLite 数据库,然后尝试查询该数据,您返回的数据可能与您输入的内容不完全相同。如果您输入垃圾,那么如果您返回不同的垃圾,您可能不会抱怨。

出于本次讨论的目的,“无效的 UTF”可能表示以下任何情况:

  • UTF-16 中的无效代理项对。

  • UTF-8 中的无效多字节序列。

  • 使用比表示单个代码点所需的更多字节的 UTF-8。(示例:将“A”编码为两字节序列 0xc1、0x01 而不是单个 0x41 字节。)

  • 嵌入字符串中的 NUL 字符 (U+0000)。

  • 组合字符的序列无效。

  • UTF-8 或 UTF-16 字节序列,对未定义的 Unicode 字符的数字进行编码。

1.1. 无效的 UTF 永远不会导致内存错误

如果您将无效的 UTF 插入到 SQLite 数据库中,则 SQLite 无法保证您可能会返回哪些文本。但它确实承诺无效的 UTF 永远不会导致内存错误(数组溢出、读取或写入未初始化的内存等),至少对于 SQLite 的内置处理而言。换句话说,无效的 UTF 不会导致 SQLite 崩溃。

当然,这个承诺只适用于核心 SQLite 组件,不适用于应用程序提供的扩展。如果应用程序添加了新的应用程序定义的 SQL 函数或虚拟表或整理序列或其他扩展,并且数据库包含无效的 UTF,则无效的 UTF 可能会传递到这些扩展中。如果无效的 UTF 导致其中一个扩展崩溃,那么这是扩展的问题,而不是 SQLite 的问题。

2.没有强制执行文本格式规则

SQLite 不会尝试强制执行 UTF 格式规则。您可以将无效的 UTF 插入到 TEXT 字段中,SQLite 不会对此报错。它尽可能地存储无效的 TEXT。SQLite 将其在世界上的角色视为存储引擎,而不是文本格式验证引擎。

3.尽最大努力保留文本

SQLite 不承诺总是保留无效的 UTF,但它确实做出了努力。一般来说,如果您将无效的 UTF 插入到 SQLite 中,只要您不要求 SQLite 以任何方式转换文本,您将得到完全相同的字节序列。

例如,如果您将一些带有无效代理项的 UTF-16LE 插入到具有PRAGMA encoding=UTF16LE的数据库表的 TEXT 列中,然后稍后使用sqlite3_column_text16()查询该列,您可能会得到完全相同的无效 UTF -16。但是如果在PRAGMA encoding=UTF8的数据库中插入同样无效的UTF-16LE 内容,则在存储时必须将内容转换为UTF8,这可能会导致内容发生不可逆的更改。或者,如果您将相同的无效 UTF-16LE 内容插入PRAGMA encoding=UTF16LE数据库,然后使用sqlite3_column_text()将其读出,则在读出期间必须发生 UTF16 到 UTF8 的转换,并且该转换可能会引入不可逆的更改。

或者,假设您所做的一切都使用 UTF-8(最常见的情况)。无效的 UTF-8 通常会通过数据库而不改变其字节序列。但是,如果您尝试使用substr()replace()等 SQL 函数转换无效的 UTF-8, 或者如果您尝试使用LIKE运算符进行字符串匹配,那么您可能会得到意想不到的结果。

因此,换句话说,SQLite 不会主动尝试颠覆您的无效文本。但是,当您要求 SQLite 对无效的 UTF 进行转换时,无法保证这些转换是可逆的,甚至是合理的。