解析sqlitedb文件中的SQLite数据库架构?

4
我编写了一个解析SQLite文件的程序,我可以从B-tree页面解析所有数据到记录、列和值,但我需要解析表的模式。我发现像数据库模式存储在页面1(根页面)中,并且我可以使用Hex Editor看到它,我找到了sqlite_master的结构,我按http://sqlite.org/fileformat2.html中所述的方式精确地读取它。

我想知道如何在数据库文件中找到sqlite_master表的第一个字节,如何检测模式的开始字节? SQLite DB头是否有任何相关内容?

编辑1(更多信息):

例如:我用十六进制编辑器打开了SQLite数据库(如果您检查我的页面大小为4096个字节,并且我在图像中标记了页面标题):

enter image description here

我标记了以05开头的根页面标题意味着该页面是内部表B-tree页面,请检查B-tree页面标题格式(http://sqlite.org/fileformat2.html,它有5个单元格,您可以使用此单元格指针数组查看它:0FFB、0FF6、0FF1、0FEC、0FE7(从结束头部开始),所有单元格都有5个字节,从0FE7开始,您可以在图像中看到模式(在文本部分)从232~240开始,我检查其他数据库和不同位置的模式...

编辑2:

您可以从https://www.dropbox.com/s/lanky02kneyb74w/31bb7ba8914766d4ba40d6dfb6113c8b614be442下载示例文件。

编辑3:

在我的文件中,您可以看到

$ hexdump -C 31bb7ba8914766d4ba40d6dfb6113c8b614be442

00000000  53 51 4c 69 74 65 20 66  6f 72 6d 61 74 20 33 00  |SQLite format 3.|
00000010  10 00 02 02 00 40 20 20  00 00 00 02 00 00 00 3f  |.....@  .......?|
00000020  00 00 00 00 00 00 00 00  00 00 00 47 00 00 00 04  |...........G....|
00000030  00 00 00 00 00 00 00 00  00 00 00 01 00 00 00 00  |................|
00000040  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000050  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 02  |................|
00000060  00 2d e2 25 05 00 00 00  05 0f e7 00 00 00 00 3d  |.-.%...........=|
00000070  0f fb 0f f6 0f f1 0f ec  0f e7 08 7f 07 9d 08 3c  |...............<|
00000080  07 01 06 22 05 92 04 fe  03 fc 04 c1 03 4d 02 b8  |...".........M..|
00000090  02 0a 02 75 01 32 01 c7  00 e9 00 e9 00 00 00 00  |...u.2..........|
000000a0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
000000e0  00 00 00 00 00 00 00 00  00 47 18 06 17 5b 35 01  |.........G...[5.|
000000f0  00 69 6e 64 65 78 73 71  6c 69 74 65 5f 61 75 74  |.indexsqlite_aut|
00000100  6f 69 6e 64 65 78 5f 41  42 4d 75 6c 74 69 56 61  |oindex_ABMultiVa|
00000110  6c 75 65 45 6e 74 72 79  4b 65 79 5f 31 41 42 4d  |lueEntryKey_1ABM|
00000120  75 6c 74 69 56 61 6c 75  65 45 6e 74 72 79 4b 65  |ultiValueEntryKe|

Page Header ( offset 64)

05          <- interior table b-tree page
0000        <- Byte offset into the page of the first freeblock
0005        <- Number of cells on this page
0FE7        <- Offset to the first byte of the cell content area
00          <- Number of fragmented free bytes
0000003D    (61) <- The right-most pointer

Cell Array Pointers & Cell Contents:
(Table Interior Cell Format)

Cell Pointer| Page number of left child | Rowid
------------|---------------------------|-------
0FFB        | 0000001A      (26)        | 15
0FF6        | 0000001C      (28)        | 2D
0FF1        | 00000031      (49)        | 3C
0FEC        | 00000039      (57)        | 48
0FE7        | 0000003C      (60)        | 4C     <- equal to (Offset to the first byte of the cell content area) in page header

你对该文件有什么具体的问题? - CL.
@CL。非常感谢你尝试解决我的问题,如果你还记得我,你曾经跟着我写过SQLite解析器以提取数据和恢复已删除的记录(https://dev59.com/L3zaa4cB1Zd3GeqPNTt3),我已经写好了它并且它可以很好地提取所有值,但现在,我只想加载表的模式,我该如何找到模式?如果你读取第一页的页头或单元指针数组(根页),你无法找到模式的偏移量,并且无法加载sqlite_master表。 - Mehdi Yeganeh
@CL,请告诉我,如果第一个表是“内部表B树页面”,你如何在SQLite数据库文件中找到模式偏移量,就像我的文件一样..!? - Mehdi Yeganeh
已更新答案。模式存储在 sqlite_master 表的 sql 列中。 - CL.
2个回答

4

我知道你的问题是一年多以前提出的,你很可能已经解决了它,但是我想提交一个答案,以防其他人有同样的问题。我和你一样陷入了同样的困境,Mehdi。我想读取SQLite数据库文件,并寻找主表/模式。它似乎在页面1中,但页眉没有指向它。造成我的困惑有两个原因。

(1) 我的SQLite数据库文件中有很多未使用的“死”数据。我认为随着数据库的创建和增长,实际活动数据的位置移动了,旧位置没有被写入零。搜索一些“CREATE TABLE”语句会在文件的不同位置发现多个结果。稍后,我确定实际模式被分成几个部分,并位于第18、10和8页(第1页内部表指向这些页)。如果不是因为第二个原因,我本来会更早地检测到这一点。

(2) 我错误地计算了页码的字节位置,这让我感到困惑。其中p =页面号,s =页面大小,我认为它是[p * s]....但实际上是[(p-1) * s](除了第1页从第100个字节开始)。换句话说,我以为页码从0开始而不是1。

另外,我认为http://sqlite.org/fileformat2.html页面缺少一些重要信息。具体来说,它没有解释模式表中“根页”号码位于哪里(在第4个字段中)。我在sqlite.org页面上找不到这个信息。


1
我不知道这个答案是否正确,但无论如何都是一个好的答案。 :) - Tech Savant

2
您提供的文档在2.6节中说:

数据库文件的第1页是一个表B树的根页面,它保存了一个名为“sqlite_master”的特殊表

并且在1.5节中说:

B树页面按以下顺序分成区域:

  1. 100字节的数据库文件头(仅在第1页上找到)
  2. 8或12字节的B树页面头…

例如,使用这个数据库:
$ sqlite3 test.db "create table hello(world);"
$ hexdump -C test.db 
00000000  53 51 4c 69 74 65 20 66  6f 72 6d 61 74 20 33 00  |SQLite format 3.|
00000010  04 00 01 01 00 40 20 20  00 00 00 01 00 00 00 02  |.....@  ........|
00000020  00 00 00 00 00 00 00 00  00 00 00 01 00 00 00 04  |................|
00000030  00 00 00 00 00 00 00 00  00 00 00 01 00 00 00 00  |................|
00000040  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000050  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 01  |................|
00000060  00 2d e6 03 0d 00 00 00  01 03 cf 00 03 cf 00 00  |.-æ.......Ï..Ï..|
00000070  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
000003c0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 2f  |.............../|
000003d0  01 06 17 17 17 01 3f 74  61 62 6c 65 68 65 6c 6c  |......?tablehell|
000003e0  6f 68 65 6c 6c 6f 02 43  52 45 41 54 45 20 54 41  |ohello.CREATE TA|
000003f0  42 4c 45 20 68 65 6c 6c  6f 28 77 6f 72 6c 64 29  |BLE hello(world)|
00000400  0d 00 00 00 00 04 00 00  00 00 00 00 00 00 00 00  |................|
00000410  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
该文本为一个 SQLite 数据库文件的十六进制表示,其中包含了创建一个名为 hello 的表格的 SQL 命令。
“...偏移量为0x64的页面标题具有以下值:”
  • 0d: 页面是叶子表b树页
  • 0000: 空闲块偏移
  • 0001: 单元格数量
  • 03cf: 单元格内容的偏移量
  • 00: 分段空闲字节
  • 03cf: 第一个单元格指针

并且在偏移量3cf处,您有一个标准的表b树叶子单元格,其中包含sqlite_master表的唯一行:

sqlite> select * from sqlite_master;
type        name        tbl_name    rootpage    sql                      
----------  ----------  ----------  ----------  -------------------------
table       hello       hello       2           CREATE TABLE hello(world)

谢谢你的帮助,我已经阅读了它,但在页面标题中找不到任何关于模式(schema)的内容,细胞指针数组也没有对我有所帮助... 模式位于未分配的区域中,请查看编辑1,再次感谢。 - Mehdi Yeganeh
我已经点赞了,但请检查我的数据库文件,它以内部表B树页面开头。请检查一下(这是iPhone的联系人数据库,您可以使用SQLite Manager打开它)... https://www.dropbox.com/s/lanky02kneyb74w/31bb7ba8914766d4ba40d6dfb6113c8b614be442 - Mehdi Yeganeh
请检查链接并帮我处理这个以“interior b-tree page”开头的文件。 - Mehdi Yeganeh
请帮我示范在我的文件例子或任何以B-tree表内开头的SQLite数据库中,如何使用tanx cl找到模式偏移。当根页是B-tree内部表时,请告诉我该如何找到模式偏移?非常感谢您的帮助。 - Mehdi Yeganeh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接