冒名顶替表

、简介

冒名顶替者表是 作为索引附加到同一个b 树的表。冒名顶替者表允许查询或修改索引的内容,就像索引是普通表一样。

Impostor 表仅用于分析和调试。这不是大多数应用程序开发人员应该了解甚至不知道的功能。冒名顶替者表仅供专家使用。

冒名顶替者表的不当使用会导致索引损坏,尽管以这种方式创建的任何损坏都可以通过运行REINDEX来修复。

2.细节

SQLite 中的每个表和每个索引都存储在数据库文件中的一个单独的 b 树中。每个 b-tree 都由其根页码标识。可以通过查询sqlite_schema 表的“rootpage”列找到任何索引或表的根页码。有关此设计的更多背景信息,请参阅索引教程文件格式文档。

通常表和索引的 B 树略有不同。表 b 树包含一个 64 位整数键和任意数据。64 位整数密钥是ROWID索引 b 树包含任意二进制键,没有数据。所以表b树和索引b树不直接兼容。

但是,WITHOUT ROWID表的 b 树与索引 b 树的格式相同。因此,可以像访问 WITHOUT ROWID 表一样访问索引 b 树。

2.1. 手动创建的冒名顶替者表

创建冒名顶替者表的一种方法是直接编辑 sqlite_schema 表以插入描述该表的新行。例如,假设模式是这样的:

CREATE TABLE t1(a INTEGER PRIMARY KEY,b TEXT,c INT, d INT);
CREATE INDEX t1bc ON t1(b,c);

与 t1bc 索引具有相同结构的 WITHOUT ROWID 表如下所示:

CREATE TABLE t2(b TEXT,c INT,a INT, PRIMARY KEY(b,c,a)) WITHOUT ROWID;

要针对索引“t1bc”创建永久冒名顶替者表“t2”,首先应通过运行“ PRAGMA writable_schema=ON ”来启用对 sqlite_schema 表的编辑。(注意观察这个 PRAGMA 附带的警告。一个错误会导致严重的数据库损坏。)然后像这样在 sqlite_schema 表中插入一个新条目:

INSERT INTO sqlite_schema(type,name,tbl_name,rootpage,sql)
 SELECT 'table','t2','t2',rootpage,
   'CREATE TABLE t2(b,c,a,PRIMARY KEY(b,c,a))WITHOUT ROWID'
   FROM sqlite_schema
  WHERE name='t1bc';

上面的 INSERT 语句向 sqlite_schema 表添加了一个新行,该表定义了一个表“t2”,该表具有与索引“t1bc”相同的磁盘格式并指向相同的 b 树。添加此 sqlite_schema 表条目后,有必要关闭并重新打开数据库以使 SQLite 重新读取模式。然后可以查询“t2”表以查看“t1bc”索引的内容。

2.1.1. 损坏的数据库

上述手动冒名顶替者表方法的一个严重问题是,在将新的“t2”条目添加到“sqlite_schema”表后,数据库文件在技术上将被破坏。“t1bc”索引和“t2”表都将指向同一个 b 树。这不会立即导致任何问题,但应该避免运行VACUUM

可以写入“t2”表,从而改变索引的内容。但这样做会使“t1bc”索引与其父表“t1”不同步。不同步的索引会导致不正确的查询结果。

由于“t2”冒名顶替者表是数据库损坏的一种形式,因此不建议手动创建冒名顶替者表。实际上,除了专业开发人员外,不鼓励所有人使用冒名顶替表,但尤其不鼓励手动创建冒名顶替表,因为它们是永久性的。

2.2. 瞬态冒名顶替者表

另一种(更安全的)创建冒名顶替者表的方法是将冒名顶替者表的条目添加到 SQLite 的内部符号表中,而不更新磁盘上的“sqlite_schema”表。这样,冒名顶替者表仅存在于单个数据库连接中,并且在重新加载模式时会自动删除。

创建临时冒名顶替者表涉及特殊的 sqlite3_test_control()调用。与所有其他 SQLite API 不同, sqlite3_test_control()接口从一个版本到下一个版本可能会发生不兼容的更改,因此不能保证下面描述的机制在未来的 SQLite 版本中工作。SQLite 开发人员不认为这是一个问题,因为不应在应用程序中使用冒名顶替表。Impostor 表仅供分析和测试使用。

要创建一个临时冒名顶替者表,首先调用 sqlite3_test_control() 如下:

sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, db, "main", 1, tnum);

“db”参数是指向数据库连接的指针。“主要”参数是要在其中创建冒名顶替者表的模式的名称。“1”参数启用冒名顶替表机制。“tnum”是冒名顶替表应该镜像的索引的根页。

在上面的 sqlite3_test_control() 调用之后,然后运行​​CREATE TABLE 语句来定义冒名顶替者表。启用冒名顶替机制后,此 CREATE TABLE 语句不会创建真正的表,而只是在 SQLite 的内部符号表中添加一个条目。请注意,CREATE TABLE 语句必须采用正确的索引格式。如果冒名顶替者表的列数错误,或者不是WITHOUT ROWID表,或者与索引 b 树不兼容,则在使用冒名顶替者表时将导致 SQLITE_CORRUPT错误。

运行 CREATE TABLE 语句后,禁用冒名顶替机制,如下所示:

sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, db, "main", 0, 0);

换句话说,进行相同的 sqlite3_test_control() 调用,只是将最后两个参数更改为零。

如上所述,将冒名顶替者表加载到 SQLite 的内部模式后,冒名顶替者表可以用作任何其他表。但是冒名顶替表将只对创建它的一个数据库连接可见。不对磁盘上的数据库文件进行任何更改。下次加载模式时,冒名顶替者表将消失。

2.3. .imposter Shell 命令

从 SQLite 3.16.0 (2017-01-02) 开始,命令行 shell包含一个点命令“.imposter”,它完成设置临时冒名顶替者表的所有工作。无需多次调用 sqlite3_test_control() 并找出并调用兼容的 CREATE TABLE 语句,可以按如下方式构造一个临时冒名顶替者表:

.imposter t1bc t2

当然,用所需的索引和冒名顶替者表名代替示例中显示的“t1bc”和“t2”。“.imposter”命令读取“t1bc”索引的架构,使用该信息为冒名顶替者表构建兼容的 CREATE TABLE 语句,然后进行所有必要的调用以自动创建临时冒名顶替者表。

3.总结和最后警告

冒名顶替者表机制是 SQLite 的强大分析和调试工具。但与所有锋利的工具一样,它也可能很危险,如果使用不当可能会导致数据库文件损坏。不要尝试在应用程序中使用冒名顶替表。Imposter 表旨在供专家在实验室中使用。