一、简介
冒名顶替者表是 作为索引附加到同一个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 表旨在供专家在实验室中使用。