从 SQLite 3.5.9 迁移到 3.6.0

SQLite 版本 3.6.0 (2008-07-16) 包含许多更改。正如 SQLite 项目的习惯,大多数更改都是完全向后兼容的。但是,3.6.0 版中的一些更改是不兼容的,可能需要修改应用程序代码和/或 makefile。本文档简要介绍 SQLite 3.6.0 的变化,特别注意不兼容的变化。

关键点:
  • 数据库文件格式不变。
  • 所有不兼容性都在模糊的接口上,因此对大多数应用程序的影响应该为零。

1.0 不兼容的更改

首先介绍不兼容的更改,因为它们对维护人员和程序员来说是最重要的。

1.1 不兼容的变化概述

  1. sqlite3_vfs对象的更改

    1. xAccess 方法的签名已被修改为返回错误代码并将其输出存储到参数指向的整数中,而不是直接返回输出。此更改允许 xAccess() 方法报告失败。与此签名更改相关联的是,添加了一个新的扩展错误代码SQLITE_IOERR_ACCESS

    2. xGetTempname 方法已从sqlite3_vfs中删除。取而代之的是,xOpen 方法得到增强,可以在文件名参数为 NULL 时打开它自己发明的临时文件。

    3. 向sqlite3_vfs添加了 xGetLastError() 方法,用于将特定于文件系统的错误消息和错误代码返回给 SQLite。

  2. sqlite3_io_methods上的 xCheckReservedLock 方法的签名 已被修改,以便它返回错误代码并将其布尔结果存储到参数指向的整数中。与此更改相关联的是,添加了一个新的扩展错误代码 SQLITE_IOERR_CHECKRESERVEDLOCK

  3. 当 SQLite 被移植到新的操作系统(除 Unix、Windows 和 OS/2 之外的操作系统,其端口与核心一起提供)时,必须提供两个新函数sqlite3_os_init()sqlite3_os_end()作为港口。

  4. IN 和 NOT IN 运算符在其右侧表达式中处理 NULL 值的方式已与 SQL 标准和其他 SQL 数据库引擎保持一致。

  5. SELECT语句的结果集的列名在某些情况下已经过调整,以更像其他 SQL 数据库引擎一样工作。

  6. 编译时选项的更改:

    1. 不再识别 SQLITE_MUTEX_APPDEF 编译时参数。作为替代, 可以在运行时使用带有SQLITE_CONFIG_MUTEX 运算符和sqlite3_mutex_methods对象的sqlite3_config()创建替代互斥实现。

    2. 编译时选项 OS_UNIX、OS_WIN、OS_OS2、OS_OTHER 和 TEMP_STORE 已重命名为包含“SQLITE_”前缀,以帮助避免命名空间与应用程序软件发生冲突。这些选项的新名称分别是:SQLITE_OS_UNIX、SQLITE_OS_WIN、SQLITE_OS_OS2、SQLITE_OS_OTHER 和SQLITE_TEMP_STORE

1.2 VFS 层的变化

SQLite 3.5.0 版引入了一个新的操作系统接口层,它提供了底层操作系统的抽象。这是一项重要的创新,已被证明有助于移植和维护 SQLite。然而,开发人员发现了 3.5.0 版中引入的原始“虚拟文件系统”设计中的一些小缺陷,因此 SQLite 3.6.0 包含一些不兼容的小更改来解决这些缺陷。

关键点: 3.6.0 版 SQLite 操作系统接口中的不兼容更改仅影响使用 虚拟文件系统接口或提供应用程序定义的互斥实现 或使用其他晦涩的编译器的少数应用程序时间选项。SQLite 3.6.0 版引入的更改对绝大多数使用 Unix、Windows 和 OS/2 内置接口并使用标准构建配置的 SQLite 应用程序的影响为零。

1.3 IN 运算符处理 NULL 的方式的变化

SQLite 的所有版本(包括 3.5.9 版)都错误处理了 IN 和 NOT IN 运算符右侧的 NULL 值。具体来说,SQLite 之前忽略了 IN 和 NOT IN 右侧的 NULL。

假设我们有一个表 X1 定义如下:

  CREATE TABLE x1(x INTEGER);
  INSERT INTO x1 VALUES(1);
  INSERT INTO x1 VALUES(2);
  INSERT INTO x1 VALUES(NULL);

鉴于上面 X1 的定义,以下表达式在 SQLite 中历来被评估为 FALSE,尽管正确答案实际上是 NULL:

  3 IN (1,2,NULL)
  3 IN (SELECT * FROM x1)

类似地,以下表达式在历史上被评估为 TRUE,而实际上 NULL 也是此处的正确答案:

  3 NOT IN (1,2,NULL)
  3 NOT IN (SELECT * FROM x1)

根据 SQL:1999 标准,SQLite 的历史行为是不正确的,并且与 MySQL 和 PostgreSQL 的行为不一致。3.6.0 版更改了 IN 和 NOT IN 运算符的行为以符合标准并提供与其他 SQL 数据库引擎相同的结果。

关键点:对 IN 和 NOT IN 运算符处理 NULL 值的方式的更改在技术上是错误修复,而不是设计更改。但是,维护者应该检查以确保应用程序在升级到版本 3.6.0 之前不依赖于旧的、有缺陷的行为。

1.4 列命名规则的变化

连接子查询报告的列名已被略微修改,以便更像其他数据库引擎一样工作。考虑以下查询:

  CREATE TABLE t1(a);
  CREATE TABLE t2(x);
  SELECT * FROM (SELECT t1.a FROM t1 JOIN t2 ORDER BY t2.x LIMIT 1) ORDER BY 1;

在版本 3.5.9 中,上面的查询将返回一个名为“t1.a”的列。在版本 3.6.0 中,列名只是“a”。

SQLite 从未对SELECT语句的结果集中列的名称做出任何承诺,除非该列包含 AS 子句。因此,这种对列名的更改在技术上不是不兼容。SQLite 只是从一种未定义的行为更改为另一种。尽管如此,许多应用程序依赖于 SQLite 未指定的列命名行为,因此在不兼容的更改子标题下讨论了此更改。

1.5 编译时选项的改变

SQLite 的编译时选项由 C 预处理器宏控制。SQLite 版本 3.6.0 更改了其中一些宏的名称,以便所有特定于 SQLite 的 C 预处理器宏都以“SQLITE_”前缀开头。这样做是为了降低与其他软件模块名称冲突的风险。

要点: 对编译时选项的更改有可能影响执行 SQLite 自定义构建的项目中的 makefile。这些更改对应用程序代码和大多数使用标准默认构建 SQLite 的项目的影响应该为零。

2.0 完全向后兼容的增强功能

除了上面列出的不兼容更改之外,SQLite 3.6.0 版还添加了以下向后兼容的更改和增强功能:

  1. 新的sqlite3_config()接口允许应用程序在运行时自定义 SQLite 的行为。使用sqlite3_config()可能的自定义包括以下内容:

    1. 使用带有sqlite3_mutex_methods对象的SQLITE_CONFIG_MUTEX动词指定替代互斥实现 。

    2. 使用带有sqlite3_mem_methods对象的SQLITE_CONFIG_MALLOC动词指定替代 malloc 实现 。

    3. 使用SQLITE_CONFIG_SINGLETHREADSQLITE_CONFIG_MULTITHREADSQLITE_CONFIG_SERIALIZED部分或完全禁用互斥锁的使用

  2. 新标志SQLITE_OPEN_NOMUTEX可用于 sqlite3_open_v2()接口。

  3. 新的sqlite3_status()接口允许应用程序在运行时查询 SQLite 的性能状态。

  4. sqlite3_memory_used ( )sqlite3_memory_highwater() 接口已弃用。等效功能现在可通过sqlite3_status()获得。

  5. 可以调用sqlite3_initialize()接口来显式初始化 SQLite 子系统。调用某些接口时会自动调用 sqlite3_initialize()接口,因此不需要使用sqlite3_initialize() ,但建议使用。

  6. sqlite3_shutdown()接口导致 SQLite 释放任何可能由sqlite3_initialize()分配的系统资源(内存分配、互斥锁、打开的文件句柄)

  7. sqlite3_next_stmt()接口允许应用程序发现与数据库连接关联的所有准备好的语句

  8. 添加了page_count PRAGMA,用于以页为单位返回基础数据库文件的大小。

  9. 添加了新的R*Tree 索引扩展