小巧、快速、可靠。
三选二。
SQLite 归档文件

1. 简介

"SQLite 归档" 是一个类似于 ZIP 归档Tarball 的文件容器,但基于 SQLite 数据库。

SQLite 归档是一个普通的 SQLite 数据库文件,它在其模式中包含以下表格

CREATE TABLE sqlar(
  name TEXT PRIMARY KEY,  -- name of the file
  mode INT,               -- access permissions
  mtime INT,              -- last modification time
  sz INT,                 -- original file size
  data BLOB               -- compressed content
);

SQLAR 表的每一行都包含单个文件的内容。文件名(相对于归档根目录的完整路径名)位于 "name" 字段中。 "mode" 字段是一个整数,表示文件的 Unix 风格访问权限。 "mtime" 是自 1970 年以来的文件的修改时间,以秒为单位。 "sz" 是文件的原始未压缩大小。 "data" 字段包含文件内容。内容通常使用 Deflate 压缩,但并非总是如此。如果 "sz" 字段等于 "data" 字段的大小,则内容以未压缩形式存储。

1.1. 数据库作为容器对象

SQLite 归档是 SQLite 数据库可以作为容器对象来保存大量较小数据组件这一更普遍概念的一个例子。

对于 PostgreSQL 或 Oracle 等客户端/服务器数据库,用户和开发人员倾向于将数据库视为一项服务或一个“节点”,而不是一个对象。这是因为数据库内容分布在服务器上的多个文件中,或者可能分布在服务集群中的多个服务器上。无法指向单个文件甚至单个目录并说“这是数据库”。

相比之下,SQLite 将所有内容存储在 磁盘上的单个文件 中。你可以指向该单个文件并说“这是数据库”。它表现得像一个对象。SQLite 数据库文件可以复制、重命名、作为电子邮件附件发送、作为 POST HTTP 请求的参数传递,或者以其他方式作为图像、文档或媒体文件等其他数据对象处理。

研究表明,许多应用程序已经将 SQLite 作为容器对象。例如,Kennedy(与 SQLite 开发人员 无关)报告说,14% 的 Android 应用程序从未写入其 SQLite 数据库。据信这些应用程序是从云端下载整个数据库,然后根据需要在本地使用这些信息。换句话说,这些应用程序将 SQLite 用作可查询的线传输格式,而不是数据库。

1.2. 使用 SQLite 归档的应用程序

The Fossil 分布式版本控制 系统允许用户将检入下载为 Tarball、ZIP 归档或 SQLite 归档。

2. SQLite 归档的优点

  1. SQLite 归档灵活。ZIP 归档和 Tarball 仅限于存储文件。SQLite 归档存储文件以及应用程序认为有用的任何其他表格和/或关系数据。

  2. SQLite 归档是事务性的。更新是原子且持久的,即使在更新过程中出现崩溃或电源故障。即使其他进程同时更新归档,读取器也会看到一致且不变的内容版本。

  3. SQLite 归档可以增量更新。可以添加、删除或替换单个文件,而无需重写整个归档。

  4. SQLite 归档可以使用高级查询语言(SQL)进行查询。一些例子

    • 归档中所有以 ".h" 或 ".cpp" 结尾的文件的总大小是多少?
    • 压缩率低于 25% 的文件占总文件的百分比是多少?
    • 归档中有多少个可执行文件?
    无需解压缩或提取任何内容即可回答类似这些(以及无数其他)问题。
  5. 已经将 SQLite 用于其他目的的应用程序可以使用一个小扩展(https://sqlite.ac.cn/src/file/ext/misc/sqlar.c)轻松添加对 SQLite 归档的支持以处理内容的压缩和解压缩。即使这个小扩展也可以省略,前提是归档中的文件未压缩。相比之下,支持 ZIP 归档和/或 Tarball 需要使用单独的库或大量额外的自定义代码,有时两者兼而有之。

  6. SQLite 归档可以绕过防火墙强加的审查。例如,某些被认为“危险”的文件类型(例如 DLL)将被 Gmail 以及可能还有许多其他电子邮件服务和防火墙屏蔽,即使这些文件包装在 ZIP 归档或 Tarball 中。但这些防火墙通常(尚未)了解 SQLite 归档,因此可以将内容放入 SQLite 归档中以逃避审查。

3. SQLite 归档的缺点

  1. SQLite 归档是一种相对较新的格式。它首次在 2014 年被描述。另一方面,ZIP 归档和 Tarball 已经存在了几十年,并且已成为标准格式。大多数程序员都知道什么是 ZIP 归档或 Tarball,但如果你说“SQLite 归档”,你更有可能得到“什么?”的回复。处理 ZIP 归档和 Tarball 的工具更有可能安装在标准计算机上。

  2. 由于 SQLite 数据库是一种更通用的格式(它旨在做更多的事情,而不仅仅是存储一堆文件),因此它不像 ZIP 归档或 Tarball 格式那样紧凑。SQLite 归档通常比等效的 ZIP 归档大 1%。Tarball 作为一个整体进行压缩,而不是像 SQLite 和 ZIP 归档那样分别压缩每个文件。出于这些原因,Tarball 往往比 ZIP 或 SQLite 归档更小。

    例如,下表显示了 SQLite 归档、ZIP 归档和 Tarball 的相对大小,它们包含 SQLite 3.22.0 源代码树中的 1,743 个文件

    SQLite 归档10,754,048
    ZIP 归档(使用 Info-ZIP 3.0)10,662,365
    ZIP 归档(使用 zipfile10,390,215
    Tarball 9,781,109
  3. SQLite 归档仅支持 Deflate 压缩方法。Tarball 和 ZIP 归档支持更广泛的压缩方法种类。

4. 从命令行管理 SQLite 归档

创建、更新、列出和提取 SQLite 归档的推荐方法是使用 sqlite3.exe 命令行 shell,用于 SQLite 版本 3.23.0(2018-04-02)或更高版本。此 CLI 支持 -A 命令行选项,可轻松管理 SQLite 归档。SQLite 版本 3.22.0(2018-01-22)的 CLI 具有 .archive 命令,用于管理 SQLite 归档,但这需要与 shell 交互。

要使用以下命令之一列出名为 "example.sqlar" 的 SQLite 归档中的所有文件

sqlite3 example.sqlar -At
sqlite3 example.sqlar -Atv

要从名为 "example.sqlar" 的 SQLite 归档中提取所有文件

sqlite3 example.sqlar -Ax

要创建一个名为 "alltxt.sqlar" 的新 SQLite 归档,其中包含当前目录中的所有 *.txt 文件

sqlite3 alltxt.sqlar -Ac *.txt

要在现有 SQLite 归档中添加或更新文件

sqlite3 example.sqlar -Au *.md

有关使用提示和所有选项的摘要,只需向 CLI 提供 -A 选项,不带任何其他参数

sqlite3 -A

如果文件名参数是 ZIP 归档而不是 SQLite 数据库,则所有这些命令都以相同的方式工作。

4.1. 其他命令行工具

正如存在用于管理 ZIP 归档的 "zip" 程序和用于管理 Tarball 的 "tar" 程序一样,"sqlar" 程序 存在于管理 SQL 归档。 "sqlar" 程序能够创建新的 SQLite 归档、列出现有归档的内容、添加或删除归档中的文件,以及从归档中提取文件。一个单独的 "sqlarfs" 程序能够将 SQLite 归档安装为 Fuse 文件系统

5. 从应用程序代码管理 SQLite 归档

应用程序可以通过链接到 SQLite 并包含 ext/misc/sqlar.c 扩展来处理压缩和解压缩,轻松地读取或写入 SQLite 归档。sqlar.c 扩展创建了两个新的 SQL 函数。

sqlar_compress(X)

sqlar_compress(X) 函数尝试使用 Default 算法压缩 blob X 的副本,并将结果作为 blob 返回。如果输入 X 不是可压缩的 blob,则返回 X 的副本。此例程用于将内容插入 SQLite 归档。

sqlar_uncompress(Y,SZ)

sqlar_uncompress(Y,SZ) 函数将撤消由 sqlar_compress(X) 完成的压缩。Y 参数是压缩内容(先前对 sqlar_compress() 的调用的输出),SZ 是生成 Y 的输入 X 的原始未压缩大小。如果 SZ 小于或等于 Y 的大小,则表示未发生压缩,因此 sqlar_uncompress(Y,SZ) 返回 Y 的副本。否则,sqlar_uncompress(Y,SZ) 对 Y 运行 Inflate 算法以对其进行解压缩并将其恢复为其原始形式,并返回解压缩的内容。此例程用于从 SQLite 归档中提取内容。

使用以上两个例程,应用程序可以轻松地将新记录插入 SQLite 归档或从 SQLite 归档中提取现有记录。使用以下代码将新记录插入 SQLite 归档

INSERT INTO sqlar(name,mode,mtime,sz,data)
 VALUES ($name,$mode,strftime('%s',$mtime),
         length($content),sqlar_compress($content));

使用以下代码从 SQLite 归档中提取条目

SELECT name, mode, datetime(mtime,'unixepoch'), sqlar_uncompress(data,sz)
  FROM sqlar
 WHERE ...;

以上代码适用于一般情况。对于仅存储未压缩或不可压缩内容的 SQLite 归档的特殊情况(例如,在仅存储 JPEG、GIF 和/或 PNG 图像的 SQLite 归档中,可能会出现这种情况),则可以将内容插入数据库或从数据库中提取内容,而无需使用 sqlar_compress() 和 sqlar_uncompress() 函数,并且不需要 sqlar.c 扩展。

此页面最后修改于 2023-01-12 11:08:35 UTC