EP.006 / TRANSCRIPT

sqlite.c逐字稿

公共领域

FINAL MASTER / release-approved

两个词

0:00

关于 SQLite,很多人知道的是两个大得有些失真的数字。

一个,是那近乎恐怖的百分之百测试覆盖率。准确地说,是核心代码在实际部署配置下,保持百分之百的分支测试覆盖。

另一个,是无法精确统计、但至少要以 billions 计的部署规模。

可这两个数字,都在讲 SQLite 已经抵达了多远。

它的首页上,还有一句更短、也更接近起点的话。

[031]

引用 [031]

supports

当前官方页面支撑核心代码在实际部署配置下的百分之百分支覆盖,以及项目方对 billions 级 copies 的估算。精确数量无法取得,copies 不等于设备装机数,覆盖率也不等于绝对无缺陷或历史上始终如此。

定位 How SQLite Is Tested sections 1.1 and 7, including '100% branch test coverage in an as-deployed configuration'; Most Widely Deployed opening paragraph and exact-ranking caveat

查看完整证据关系

“SQLite source code is in the public-domain and is free to everyone to use for any purpose.”

SQLite 的源代码属于公共领域。任何人,都可以把它用于任何目的。

[001]

引用 [001]

supports

当前首页原句支撑本期命题;同页关于设备数量和普及程度的文案不随这条引用进入节目事实。

定位 homepage paragraph below the main product description, source-code and public-domain links

查看完整证据关系

这句话最重的地方,甚至不是“公共领域”。

是两个没有留下例外的词。

任何人。

任何目的。

不必先成为客户,不必加入一个基金会,不必在许可证清单里找到自己的用途,也不必写信询问作者:我可以把它装进一部手机吗?可以修改吗?可以收费出售吗?可以放进一套作者永远不会见到的系统吗?

从权利上说,都可以。

可是下一位使用 SQLite 的人,也许此刻还没有开始写他的产品。维护者不知道他是谁,在哪个国家,使用哪一种编译器,公司的律师会提出什么问题。

如果承诺真的是“任何人”,就不能等他出现以后再单独批准。

如果承诺真的是“任何目的”,它就不能只停留在网页上。代码必须能离开作者的电脑,进入另一棵源码树,被另一家公司修改和编译,藏进另一件产品,再由完全不认识 SQLite 的人使用。

一句话,要怎样获得这样的现实条件?

先让数据库跟着应用走

2:14

故事的起点,恰好是一群不能决定数据库命运的人。

二零零零年,D. Richard Hipp 是一名做定制软件的开发者。他参与的一套应用部署在舰船上,程序需要读取数据库里的资料。数据库服务已经安装好,却由另一支团队管理。

多年以后,Hipp 回忆,服务一旦停止,应用能做的只有报告无法连接。应用团队不能进入数据库服务器,也不能直接修复它。他们可以说明问题,却无法控制提供数据的那台服务。

这里不需要把一次服务故障写成灾难。真正改变 Hipp 判断的,是一条很普通的工程边界:程序依赖数据库,但写程序的人没有数据库的控制权。

[002]

引用 [002]

supports

Hipp 2021 年后见叙述支撑舰船应用、既有数据库服务和团队无控制权;主持人补充的舰名、承包关系与具体数据库产品不进入本段。

定位 CoRecursive #066 page transcript, Richard Hipp account in the origin section before the first implementation discussion

查看完整证据关系

Hipp 注意到,这套程序并不需要一台远端服务器替它长期处理复杂事务。它主要是把数据读进内存,在应用里继续工作。

于是问题缩小了:为什么程序不能自己打开磁盘上的数据库文件?为什么读取一份本地数据,还要先请求另一个进程、另一台机器和另一位管理员?

SQLite 后来的官方文档把这种结构叫作 serverless。数据库引擎不是独立服务,而是和应用运行在同一个进程里,直接读取和写入数据库文件。这里的“没有服务器”不是没有提供网络服务的应用,而是数据库引擎本身不再隔着一条 client/server 边界。

这一步先移走的,不是许可证,而是运行时必须请求的人。

[003]

引用 [003]

supports

本人后见回忆支撑当时的问题,当前官方文档支撑技术模型;不能把今天的 serverless / zero-configuration 文案写成 Hipp 在 2000 年已经使用的完整路线图。

定位 CoRecursive #066 origin account; SQLite Is Serverless sections 1-2; Zero-Configuration explanation of no setup, server process, or administrator

查看完整证据关系

把数据库放进应用进程,听起来只像少开一个服务。实际改变的是故障与控制的边界。

在 client/server 结构里,应用先把 SQL 请求交给数据库服务,再等待另一端处理和返回。连接、服务进程、账户配置和服务器状态,都可能成为一次本地操作之外的条件。

SQLite 把这条路径缩回一台设备。应用调用库函数,数据库引擎就在进程内部工作,数据通常也在同一台设备的文件中。没有独立服务需要安装和启动,也没有一位数据库管理员必须先替这个应用准备运行环境。

这不保证程序永远不会失败。磁盘、锁、断电和应用自己的错误仍然存在。它改变的是,应用团队终于可以把数据库和自己的程序作为一个交付物一起测试,一起部署,也一起承担后果。

对“任何人”来说,这是很早却很容易被忽略的一步:能够使用一项技术,首先意味着不用等待另一个组织替你把它运行起来。

[027]

引用 [027]

supports

当前官方技术页面支撑进程内库、直接文件访问与无需独立服务配置;故障责任变化是节目基于该结构作出的工程分析,不等于无故障承诺。

定位 SQLite Is Serverless section 2 direct file access model and comparison diagram; Zero-Configuration list of absent setup and administration steps

查看完整证据关系

官方历史页把 SQLite 项目的起点记在二零零零年五月二十九日。

它最初不是要建立一家数据库公司,也不是要替代所有大型数据库。它从更小的目标开始:让一个应用拥有自己需要的数据能力,不再因为外部数据库服务失去控制。

这已经让“任何目的”获得了第一项技术条件。数据库可以进入设备,跟着应用运行。

但跟着应用走的,不只有代码,还有代码的权利。

[004]

引用 [004]

supports

官方历史页支撑项目起始日,Hipp 回忆支撑问题背景;不开列后来成就,也不把后来的公司与制度倒写成初始目标。

定位 historical SQLite Developers page entry for Hipp and project start; CoRecursive #066 origin section

查看完整证据关系

SQLite 第一版使用 GDBM,也就是 GNU Database Manager,作为底层存储。它采用哈希结构,并受 GPL 约束。

Hipp 后来想加入范围查询。只按哈希查找一个键还不够,数据需要保持顺序,查询才能找到一段连续范围。他研究过当时另一套现成存储库的文档,最后判断,与其继续试探接口,不如自己写一棵 B-tree。

于是,SQLite 失去了一项外部依赖,也获得了一项此前没有的能力。更重要的是,准备交付的核心实现开始变成 Hipp 能够说明权利来源的代码。

[005]

引用 [005]

supports

2016 年本人后见叙述支撑 GDBM、GPL、范围查询与自写 B-tree 的关系;本期只作带归属的转述,不把其他 GPL 软件描述成错误选择。

定位 official transcript lines 227-245 and 473-491, Hipp account of GDBM, range queries, self-written B-tree, and later public-domain choice

查看完整证据关系

Hipp 后来回忆,底层存储变成自己的实现以后,整份核心代码的授权选择终于回到他自己手里。然后,他把答案说得很轻。

D. Richard Hipp(原声): And I looked at the BSD license, and I looked at the MIT license and I thought, “You know, really, what's the point?” Why not just say, “Hey, it's public domain” and put it out there? And that's what I did.

这个反问,不是在否认 BSD 或 MIT 的商业友好。它们本来就是宽松的开源许可证。Hipp 选择的是再往前一步:尽量不要求下游携带来自 SQLite 的许可条件。

这项选择也把风险集中回来。只要将来混入一段权利不清的代码,SQLite 就不能继续对下一位使用者作出同样简单的承诺。

在二零零一年,最后这项代价还没有出现在故事里。此刻能看见的,只是输出端的门被推开了。

[028]

引用 [028]

supports

Hipp 2016 年后见叙述支撑 BSD / MIT / public domain 的选择理由,同期 check-in 只固定转折时间;不贬低其他宽松许可证,也不提前使用现行贡献制度。

定位 Changelog official transcript public-domain discussion beginning around 56:01; Fossil check-in 4e926efe2b

查看完整证据关系

不卖许可,出售时间

7:34

许可桌被移开以后,电话开始打进来。

Hipp 在二零一六年回忆,早期有一通电话来自 Motorola。对方希望把 SQLite 放进手机,也需要一些增强和后续支持。

Motorola 不必购买把公共领域代码复制进手机的权利。那项权利,别人也有。它真正愿意付钱购买的,是一个熟悉这套代码的人愿意为具体需求工作,并在产品继续推进时留下来回答问题。

我们不知道 Motorola 当时比较过哪些方案,也不知道它为什么具体选择 SQLite。这份合同对应哪一种手机、哪一次出货,同样没有 Motorola 一侧的同期记录。因此,这里只能保留 Hipp 多年后的讲述:对方要把数据库放进手机,现有版本还需要增强,产品也需要后续支持。

到那时,SQLite 已经可以跟着应用运行,公共领域也已经允许商业内嵌。它们是这通电话发生以前已经成立的条件,却不能冒充 Motorola 留下的选型理由。

但这段讲述让“任何目的”第一次穿过一条重要边界:商业目的没有被排除。免费使用与付费合作,也没有互相抵消。

[007]

引用 [007]

supports

Hipp 2016 年后见回忆支撑商业采用与支持合同;缺 Motorola 侧材料,不能补写型号、合同日期、金额、功能或实际出货范围。

定位 official transcript lines 237-247, Hipp account of Motorola requesting phone enhancements, support, and a contract

查看完整证据关系

公共领域没有让维护工作消失。恰好相反,它让更多人可以先把代码带进产品,然后带着真实需求回来。

复制一份源码,成本可以接近于零。理解一个边界条件、修改一个存储引擎、追查一个罕见错误、承诺几年以后仍有人响应,成本不会因为版权被放弃而消失。

SQLite 的商业关系由此获得了一个特别的位置:维护者不能用“不给许可证”迫使使用者付费;他们只能出售使用者确实需要的时间、功能、测试和保证。

这使一份 SQLite 合同和常见的按席位授权不同。

合同签订以前,客户已经可以取得代码。谈判失败以后,它也没有因此失去公共领域的使用权。维护者不能关闭下载按钮,不能让旧副本停止运行,也不能以产品用途超出许可为由收回代码。

客户真正需要谈判的,是尚未存在的功能由谁实现,故障出现时谁来诊断,某一种平台能否得到持续支持,以及维护者愿意为这段关系投入多少时间。

公共领域把商业交换从“准不准使用”推向“谁来承担工作”。这也解释了为什么 SQLite 可以同时看起来完全免费,又逐渐形成一支以支持和维护为职业的团队。

下一位采用方带来的,不是同一种问题。

Hipp 回忆,AOL 联系他时,需要 SQLite 能够处理二进制数据。当时的 SQLite 二点零只能处理文本。AOL 为这项增强提供资金,SQLite 因而要补上一种真实产品已经需要、现有版本却没有的能力。

现有材料没有留下 AOL 的具体产品,也不能把 SQLite 三点零的全部变化归因于这一家公司。能够确认的是更短的一条因果:允许商业使用,只解决了“能不能拿走”;采用方能否真正留下来,还取决于数据库能不能处理它的资料。

[030]

引用 [030]

supports

Hipp 2016 年后见回忆支撑 AOL 的二进制数据需求与付费增强;缺 AOL 侧材料,不补具体产品,也不把 AOL 写成 SQLite 3 的单一原因。

定位 official transcript lines 241-245, Hipp account of AOL requesting binary-data support and funding enhancements

查看完整证据关系

这种需求也开始超过一个人能够长期承担的范围。

SQLite 的历史 Developers 页面称,Dan Kennedy 从二零零二年起成为 key contributor。Hipp 后来把 Kennedy 开始为他工作的时间放在 AOL 资助功能增强的阶段,并说 Kennedy 写下了 SQLite 的重要部分。

两个材料给出的关系起点并不完全相同,所以不能把某一通客户电话写成唯一原因。能够确定的是,SQLite 很早就不再是 Hipp 一个人的代码;当“任何目的”把项目带进更多产品,维护承诺也开始由一个小团队承担。

[008]

引用 [008]

supports

材料共同支撑 Kennedy 的长期核心参与;2002 年 key contributor 与 AOL 阶段工作关系不强行合并为同一精确日期,Fossil 记录也不当作工作量统计。

定位 historical Developers page Dan Kennedy entry; Changelog transcript lines 241-245; Fossil timeline filtered to user dan in 2017-02

查看完整证据关系

这里已经出现了本期反复回来的关系。

下游可以拥有自己的 SQLite 副本,却不因此拥有维护团队。客户可以为一项功能和支持付费,却不因此拥有所有未来版本。核心团队可以接受真实产品带来的资金和问题,却仍要让没有付费、甚至从未联系过他们的人继续使用同一份公共代码。

商业采用没有替首页那句承诺划出会员区。它开始为承诺提供人和时间。

数据库从界面上消失

11:52

手机仍然让我们想象得到 SQLite 在哪里:它在设备里,替某个应用保存数据。

浏览器把这件事推进得更隐蔽。

二零零四年六月,Mozilla 开发者 Mike Shaver 在 Bugzilla 提议,用 SQLite 替换浏览历史原来的 Mork 后端。他没有只写“SQLite 更好”,而是列出这次替换要减少的工作:获得索引,不必在业务代码里重新实现分组一类的 SQL 操作,可以使用现成工具;正确使用时可以支持多进程和多线程,而且数据库本身不必由 Mozilla 维护。

这些是采用方在动手时写下的理由。九月,另一项 bug 开始建立最初的 mozStorage 接口。十月,基于 mozStorage 的 history 实现继续推进,最终被标记为修复完成。

这不是把 SQLite 做成浏览器里的一个新按钮。恰恰相反,mozStorage 在它外面增加了一层属于 Mozilla 的接口。浏览器其余部分面对的是 Mozilla 的存储组件,SQLite 退到实现内部。

[009]

引用 [009]

supports

Bug 245745 comment 0 支撑 Mike Shaver 当时列出的索引、SQL、工具、并发使用和免于自行维护等理由,后续同期 issue 支撑 mozStorage 与 history 实现推进;早期 patch 不等于 Firefox 3 最终 Places 的全部设计。

定位 Bugzilla bugs 245745, 261861, and 266174: summaries, creation dates, dependency links, comments, and final resolution state

查看完整证据关系

Mozilla 后来的 Places 文档又从成品一侧回顾了旧后端的阻力:某些搜索和维护操作很慢,RDF 面向文件系统的一侧会出现损坏或意外状态,分离的数据不利于交叉使用,RDF 的维护前景也不清楚。这里必须分开两层时间:前一组是 Mike Shaver 在二零零四年的同期提案,后一组是采用方后来对 Firefox 三 Places 的总结。

最终用户安装 Firefox 时,不会收到第二个问题:你是否同意另外安装 SQLite?

他选择的是浏览器。浏览器团队已经替他取得源码、完成集成、决定版本、编译产品,并承担数据库如何保存历史与书签的工程判断。

Firefox 后来的源码文档回顾,Firefox 二的历史和书签使用分离的 RDF 数据库;Firefox 三以 SQLite 为后端实现 Places。

到这里,“任何人”出现了新的含义。直接行使源码自由的是 Mozilla 的开发者,真正得到使用结果的,却是大量不必知道 SQLite 名字的浏览器用户。

[010]

引用 [010]

supports

采用方后见文档支撑旧后端阻力和 Firefox 3 的内部 SQLite 后端;不把它伪装成 2004 年同期原话,不由此推算用户总数,也不声称用户本人直接下载或行使 SQLite 源码权利。

定位 Firefox Source Docs, Places overview History and Bookmarks section on Firefox 2 RDF databases, performance, reliability, flexibility, maintainability, and Firefox 3 Places

查看完整证据关系

这种传播很难用下载量看清。

一个开发者从 sqlite.org 下载源码,是一次可见的选择。一个浏览器团队把 SQLite 编进产品,此后每一次浏览器安装都可能带来新的运行实例,却不再发生一次由最终用户发起的 SQLite 下载。

使用者甚至可能通过浏览器按钮删除历史、搜索书签、恢复会话,始终只面对宿主产品的界面。SQLite 在执行这些产品功能时存在,在用户的产品清单里却不存在。

公共领域在这里产生的不是知名度,而是可被隐藏的能力。下游越完整地替它完成集成,SQLite 自己越不需要以独立产品的身份出现。

一套公共领域代码可以进入产品,还不等于一家平台敢把未来押在它上面。

Hipp 后来回忆,大约二零零五年,Symbian 需要为手机操作系统选择一套数据库。对方把十种嵌入式数据库放进一次 bake-off,最后选择了 SQLite。

Hipp 同时明确说,他不知道那次评测的具体标准。我们因此只能确认三件事:操作系统需要数据库,Symbian 比较了候选方案,SQLite 被选中。不能拿 SQLite 后来公开的优点,替 Symbian 补写一张已经丢失的评分表。

Symbian 的选择目前只有 Hipp 的后见回忆,不能写成已经取得对方会议记录的同期现场。

[011]

引用 [011]

supports

Hipp 2016 年后见叙述分别支撑 Symbian 初次选择与采用后的持续性问题;缺 Symbian 侧材料,且 Hipp 明说不知道 bake-off 标准。

定位 official transcript lines 299-303 on Symbian database need, bake-off and unknown criteria; lines 521-529 on critical-infrastructure continuity and orphanware concern

查看完整证据关系

采用以后,问题变了。

到二零零七年前后,Symbian 正准备把 SQLite 放进手机基础设施。Hipp 后来这样回忆。

D. Richard Hipp(原声): But they realized that if this is a critical part of their infrastructure, they needed to make sure my business was sustainable.

这已经不是初次选型标准,而是继续依赖的条件。关键产品需要的,不只是一份今天可以编译的源码,还要有人在明天解释故障、维护旧版本、制作定制测试,并让知识不只存在于一个人身上。

这段担忧仍然来自 Hipp 的后见讲述;随后出现的制度,则可以由协议独立核对。

[011]

引用 [011]

supports

Hipp 2016 年后见叙述分别支撑 Symbian 初次选择与采用后的持续性问题;缺 Symbian 侧材料,且 Hipp 明说不知道 bake-off 标准。

定位 official transcript lines 299-303 on Symbian database need, bake-off and unknown criteria; lines 521-529 on critical-infrastructure continuity and orphanware concern

查看完整证据关系

二零零七年的 SQLite Consortium 协议模板,把这种担忧写成了可以购买的服务。

协议要求至少保持两名合格开发者;成员可以获得支持、调试、定制开发、定制构建、旧版本支持和定制测试;成员报告或关注的 bug 可以得到优先处理。

但资金没有买走官方主线。协议同时把最终代码责任和权力留给 SQLite Architect,成员不能直接要求某一种实现必须进入原本。

这是一种不对称的交换。采用方用资金降低无人维护的风险,核心团队用支持和测试回应真实产品;技术控制仍然集中,公共领域的使用权仍然向所有人开放。

[012]

引用 [012]

supports

官方协议模板支撑制度设计,不证明每一位成员的实际履约细节;当前宣传页只作现行交叉。

定位 2007 Consortium Agreement sections 1-5 on purpose, qualified developers, services, bug priority, and architect authority; current Consortium overview

查看完整证据关系

Consortium 给首页承诺增加的,不是新的使用者身份,而是时间长度。

源码在某一天可以下载,只能证明那一天的自由。平台把数据库写进基础设施以后,还需要确认旧版本有人理解,罕见错误有人追查,新环境出现时有人能制作测试和修改。

至少两名合格开发者的要求,也让“团队”不再只是项目介绍里的署名。它成为降低单点风险的一项合同条件。

任何人仍然不需要加入 Consortium。成员付费购买的是更确定的响应和持续性,其他使用者则从同一支团队继续维护的公共交付物中受益。

浏览器内部组件和手机平台让 SQLite 获得了一条不同于普通软件产品的传播路径。

它不一定以自己的名字、安装器和图标抵达用户。经常是浏览器、操作系统、语言接口或应用先把它装进自己的交付物,再把运行结果带到设备上。

这也是为什么不能简单说“多数 SQLite 都作为依赖部署”。世界上没有一张能够计算所有实例的清单,“依赖”也混合了 vendored source、平台组件、语言 binding 和应用内部实现。

我们能确认的是两个沿时间线继续向前的结果:Firefox 三把它放进 Places;Android 从最早的平台 API 起提供 SQLite。现有 AOSP 材料能证明 SQLite 已经成为平台组件,却没有保存 Android 最初为什么从候选中选择它。这个动机缺口不能用今天的 AOSP 文档倒填。

没有全球分母,仍然可以看见同一种动作:下游产品替最终用户完成了部署。

[013]

引用 [013]

supports

两个采用方材料支撑具体传播结果;不把案例集合包装成多数部署或全球比例,也不从 API 表倒推 Android 最初选型原因。

定位 Firefox Places overview; AOSP android.database.sqlite API-version table beginning at API 1

查看完整证据关系

把使用权做成一份文件

18:39

到这里,法律允许代码进入产品,产品也愿意替它完成部署。

但“有权拿走”仍然不等于“容易拿走”。

一个真实项目可能由许多 C 文件、代码生成器、语法文件、构建脚本和平台选项组成。把整棵开发源码树搬进另一家公司,意味着还要理解它怎样生成、怎样配置、哪些文件才是最终交付物。

SQLite 需要把抽象的使用权,变成下游工程师可以执行的物理动作。

官方给出的答案,是 amalgamation。

维护者平时工作的 canonical source 仍由许多文件组成。发布时,这些源码被合成为一个巨大的 C 文件:sqlite3.c。另有一个对外声明接口的 sqlite3.h

sqlite3.c 不是人类维护的原本。它是由 canonical source 生成的构建产物,也是一整个 C translation unit。下游可以把这两个文件放进自己的 source tree,用自己的 Makefile、编译器和选项,和产品其余代码一起构建。

我们这一期的标题写成《sqlite.c》,故意少了一个数字。它指的不是仓库里真实存在的文件名,而是这种分发方式:一套数据库变成一份可以搬走的 C 源码材料。

[014]

引用 [014]

supports

官方构建文档支撑生成关系、单 translation unit 和下游 source-tree 用法;标题 `sqlite.c` 是节目简化命名,实际文件为 `sqlite3.c`。

定位 The SQLite Amalgamation sections 1-2; Amalgamation Versus Canonical Sources sections 1-4; How To Compile SQLite section 2

查看完整证据关系

这条分发链上,因此同时存在两棵源码树。

第一棵在 SQLite 维护者手里。语法、测试、工具和多个源文件在这里演化,Fossil 保存它的 canonical 历史。

第二棵属于下游产品。那里也许有浏览器渲染代码、操作系统框架、设备适配和公司自己的构建规则。sqlite3.csqlite3.h 被放进去以后,就和这棵树里的其他代码一起接受版本锁定、编译选项和发布流程。

amalgamation 像一只标准化的接口箱。上游不必教会每一个采用方怎样运行自己的全部代码生成过程,下游也不必把 SQLite 的开发仓库原样变成产品仓库。

文件看起来很大,协作边界却被压缩成两个动作:取得生成物,把它交给 C 编译器。

[029]

引用 [029]

supports

官方文档支撑 canonical source、生成物与下游 source tree 的分工;接口箱是节目类比,不表示所有下游只需两个文件或无需配置测试。

定位 Amalgamation Versus Canonical Sources sections 1-4 and diagrams; How To Compile SQLite sections 1-2; How To Download Canonical Source urtext and verification sections

查看完整证据关系

二零一六年的访谈里,Hipp 先提到一位曾经参与 SQLite 团队的开发者,Shane Harrelson,然后把 amalgamation 的构想归功于他。

这句归功很重要。sqlite3.c 很容易让人产生一个错觉:一个文件,看起来像一个人的作品。可文件的单一,来自构建过程的合成,不代表设计、实现和维护只属于一个人。

现有材料没有闭合这个构想第一次出现的年份,所以不能把二零一六年写成发明时间。我们只能说,到了那次回顾里,Hipp 明确把这个分发构想交还给另一位参与者。

一个文件减少的是下游搬运时必须理解的边界,不是上游真实存在的协作与复杂度。

[015]

引用 [015]

supports

2016 年本人后见回忆支撑 Harrelson 曾参与团队及 amalgamation 归功;首次实现年份未闭合,不把访谈发布日期当作发明日期,也不作直接引语。

定位 official transcript amalgamation discussion in the later distribution section, Hipp attribution to Shane Harrelson

查看完整证据关系

二零一九年,SQLite 又多了一扇更熟悉的门:官方 GitHub 镜像。

在 GitHub 上,任何人都可以浏览源码、克隆仓库、创建自己的分支。网页甚至摆着 pull request 的入口。可是 SQLite 官方文档明确说,这是一份从 Fossil canonical repository 单向导出的只读镜像。内容由 Fossil 输入,不经 GitHub 接受修改。

这扇门像玻璃。代码向外看得更清楚,也更容易被复制;从 GitHub 向官方原本写回的通道,仍然关闭。

此时还不能急着把它解释成维护者拒绝社区。我们只知道,SQLite 把“拿走代码”和“改变下一份官方代码”做成了两个不同动作。

[016]

引用 [016]

supports

官方文档支撑 2019-03-20、单向只读镜像和不经 GitHub 接收变更;不能把当前全部 Git/Fossil 理由倒推到早期项目。

定位 Why SQLite Does Not Use Git section 3.1, Official GitHub Mirror; mirror README sections The SQLite Source Repository and Contributing To SQLite

查看完整证据关系

代码被拿走以后,会发生什么,可以在今天的 Android 开源项目里看见。

这些材料没有记录 Android 当初为什么选择 SQLite。它们回答的是另一个时期的问题:SQLite 已经成为平台组件以后,维护者为什么不能只换掉一个版本号就结束工作?

AOSP 的 platform/external/sqlite 把 SQLite 放在自己的源码树中。升级文档要求维护者取得上游的 amalgamation 归档,导入新版本,再应用 Android 自己的 patch。如果上游变化让 patch 不能干净套用,下游要处理冲突。版本变化以后,还要更新 CTS 里的 SQLite 版本测试。

Android 已经有自己的 patch、对外 API 和兼容测试。继续采用 SQLite,意味着既要取得上游变化,又要守住这些平台差异和兼容口径。它取得了修改自由,也因此拥有自己的版本、构建和验证责任。

[017]

引用 [017]

supports

采用方源码文档支撑 vendoring、Android.patch、升级冲突与 CTS;2026 快照不代表所有设备使用同一 SQLite 版本或同一厂商构建。

定位 AOSP platform/external/sqlite tree; dist/README-Android; README-upgrade.md import, patch, conflict, version-file, and CTS steps; framework package version note

查看完整证据关系

当代码进入这种规模的产品,采用方自己也变成了一层维护者。

它要决定何时追上上游,哪些差异必须保留,补丁冲突怎样解决,平台对外暴露的 API 是否仍然兼容。最终设备上的行为,来自 SQLite 上游和 Android 下游两条演化路径的共同结果。

这时,用户得到的已经不是“sqlite.org 当前版本”,而是宿主产品在某个时间点选择、修改、测试并交付的 SQLite。

公共领域允许这层距离存在。amalgamation 让这层距离容易建立。AOSP 的升级文档则留下了距离必须被持续管理的证据。

这才是“任何目的”更完整的一层。

自由不只是允许下游修改,还允许修改以后不把差异交还上游。Android 可以为了平台需要保留自己的 patch,另一家公司也可以在内部建立完全不同的配置。

但自由没有替它们维护这些分叉。上游版本继续变化,补丁可能冲突,测试需要更新,安全修复必须重新评估。

sqlite3.c 把代码搬出官方项目的门槛压得很低。跨过门以后,每一个下游仍然要为自己的产品负责。

问题终于来到那扇只读的玻璃门前:既然任何人都能任意修改,SQLite 为什么不让这些修改同样容易地回到官方原本?

保险柜里的公共领域

24:40

紧接着,页面把镜头转了过来。

不是继续列举使用者还能做什么,而是追问写下代码的人是谁。

页面说,SQLite 的所有代码作者,以及他们所在公司的代表,都签署了 affidavit,把各自贡献投入公共领域。签字文件的原件,被保存在 Hwaci 主办公室的一只 firesafe 里。

我们不知道这只保险柜的品牌、尺寸、颜色,也不知道它放在哪一个房间。网页没有提供这些细节,本期也不会替它发明画面。

能够确认的物理事实只有一个:一项看起来没有纸面的自由,靠一批纸面原件维持。

[019]

引用 [019]

supports

当前版权页支撑作者、雇主代表、affidavit 原件和 firesafe;不扩写保险柜外观、办公室位置、签署过程或具体公司文件。

定位 SQLite Is Public Domain section, affidavit and firesafe paragraphs immediately before the usage grant

查看完整证据关系

affidavit 可以理解为一份经过宣誓的书面声明。

SQLite 公布的 Copyright Release 模板把它要解决的问题写得更具体。签署者把已经写下的贡献投入公共领域,也处理今后为了 SQLite 发布而产生的修改;签署者还要说明,这些内容由自己原创,或者来自已经确认的公共领域作品。

如果贡献者是代表雇主工作,权利问题就不能只停在个人账户的一句“我同意”。公司是否拥有相关权利、是否授权释放,也必须进入证明链。

一条 commit 可以证明谁按下了提交按钮。它不一定能证明那个人拥有把代码交给全世界任意使用的权利。

保险柜保存的,正是 Git 历史无法独自回答的部分。

[020]

引用 [020]

supports

公开模板支撑权利声明范围;模板不证明每一位历史贡献者都签署当前这一版本,也不替代具体法域法律意见。

定位 Copyright Release template dedication, future modifications, originality, and employer acknowledgement clauses; Copyright page affidavit summary

查看完整证据关系

这条证明链之所以重要,是因为 SQLite 承诺的不是“绝大部分代码大概没问题”。

版权页说,所有 deliverable code 都能追溯到原始作者,而且这些作者都完成了公共领域声明。源码镜像里的 LICENSE 文件又补充了范围:SQLite 的主要源码、测试、扩展以及实际构建输出属于这项公共领域声明;仓库里少量用于构建的辅助逻辑可能使用其他开放许可证。

这是一条需要小心表达的边界。不能把“SQLite 代码是公共领域”扩展成“sqlite.org 上每一个字节都没有版权”,更不能把第三方扩展和采访材料一起装进去。

真正被承诺的是能够进入交付物的那条代码链。

[021]

引用 [021]

supports

两个官方材料共同限定公共领域范围;不能扩展到网站内容、商标、第三方扩展、采访或全部构建辅助文件。

定位 Copyright page deliverable-code paragraph; source mirror LICENSE.md scope and build-logic exception paragraphs

查看完整证据关系

现在,把一份来自网络的 patch 放到这条链上。

代码也许完全正确,测试也许全部通过。可是维护者仍然需要知道:是谁写的?是在个人时间完成,还是属于他的雇主?里面有没有从另一份带许可证的项目复制过来的实现?作者有没有权利把它投入公共领域?几年以后,下一位使用者能不能继续不问许可?

只要这些问题有一项不清楚,首页的“任何人”就多出一项隐藏作业:请先审计这份 patch 的版权。

这正是 SQLite 输入端最深的阻力。它防的不是别人使用代码,而是来历不明的权利进入下一份官方代码。

[022]

引用 [022]

supports

官方当前政策支撑随机 patch、权利成本、proof-of-concept、重写与公开反馈入口;公开模板证明正式贡献路径存在,不能把现行措辞伪装成 2001 年口号,也不能概括为绝对零外部贡献。

定位 Copyright page Open-Source, not Open-Contribution section; Copyright Release template; source repository README contributing and forum sections; User Forum about page

查看完整证据关系

所以,copyright 页面使用了一句故意显得刺耳的标题:Open-Source, not Open-Contribution。

页面说,SQLite 不接受互联网上随机提交的 patch。完成正式权利流程成本很高,小修改通常不值得这样做。外部 patch 可以作为 proof of concept,告诉维护者问题可以怎样解决,但核心团队可能查看思路以后,从头重新实现。

这不是说外界不能报告 bug、提出需求或展示代码。SQLite 的 Forum 和 bugs forum 都是公开入口。它说的是另一件事:提供一个想法,与让某一行外部代码获得 canonical 身份,不是同一项权利。

“not Open-Contribution”也不能被压缩成“SQLite 从不接受外部贡献”。项目公开的 Copyright Release 模板本身,就证明外部代码存在一条正式入口;只是作者、雇主、原创性和公共领域声明都要进入权利流程。

门没有焊死。它拒绝的是把随机网络 patch 默认并入官方原本,而不是拒绝所有外部报告、想法或代码。对未来使用者来说,这套严格的证件检查,正是首页那句简单承诺的一部分。

[022]

引用 [022]

supports

官方当前政策支撑随机 patch、权利成本、proof-of-concept、重写与公开反馈入口;公开模板证明正式贡献路径存在,不能把现行措辞伪装成 2001 年口号,也不能概括为绝对零外部贡献。

定位 Copyright page Open-Source, not Open-Contribution section; Copyright Release template; source repository README contributing and forum sections; User Forum about page

查看完整证据关系

即使每一位作者都完成了声明,“公共领域”在所有地方也不一定产生同样顺滑的法务结果。

SQLite 的版权页列出几种情况:有的公司需要版权侵权赔偿承诺;有的法域不承认公共领域,或者不允许作者把作品投入公共领域;有的采购流程必须拿到一份有形文件,证明公司有权使用和分发这套代码。

这些组织并不是被排除在“任何人”之外。SQLite 为它们准备了 Warranty of Title。

[024]

引用 [024]

supports

当前版权页支撑项目列出的法务场景;它是项目方商业与权利说明,不是本节目对各法域作出的法律结论。

定位 Warranty of Title section, listed reasons for purchasing including indemnity, non-recognition of public domain, and tangible legal document

查看完整证据关系

Warranty of Title 不是一份只有付费客户才能获得的 SQLite 使用许可。

页面先明确:SQLite 属于公共领域,不需要许可证。随后才说明,有额外法务需求的组织可以购买一份权利保证。文件主张签署方有权把 SQLite library 投入公共领域,也提供相应保证。销售收入继续用于资助 SQLite 的改进和支持。

这条商业路径没有在首页的承诺后面补上一句“企业除外”。它承认,不同使用者抵达同一项自由时,面对的法域和组织程序并不相同,于是为更高的证明需求增加一座桥。

免费的,是使用权。收费的,是额外的证明、保证和责任承担。

[025]

引用 [025]

supports

当前项目页面支撑免费使用权与付费权利保证的区分;不据此推断任何具体客户购买、法律结果或收入规模。

定位 Warranty of Title section, no-license preface, document description, and proceeds-fund-SQLite closing paragraph

查看完整证据关系

现在,终于可以看清那只保险柜在故事里的位置。

它没有把 sqlite3.c 锁起来。源码仍然可以从网站、归档和镜像离开,进入手机、浏览器、操作系统和尚未出现的产品。

被锁进去的,是作者和权利证明。

随机 patch 不能轻易进入,不是因为公共领域承诺太窄,而是因为它太宽。只要官方原本混入一段无法确认权利来源的代码,未来每一位使用者都可能重新面对许可、赔偿和审计问题。

要让外面的代码不带钥匙就能被拿走,里面的每一张纸就不能丢。

任何目的,不是每一种负载

31:18

首页还有一个容易被误解的词,需要在结尾收紧。

“用于任何目的”,说的是你不必向 SQLite 请求用途许可。它不等于 SQLite 适合任何技术架构。

SQLite 自己的 Appropriate Uses 页面写得很直接:如果许多客户端要通过网络同时直接访问同一个数据库,更适合 client/server 数据库;写入量很大的多服务器网站,应该考虑另一种引擎;SQLite 同一时间对一个数据库文件只允许一个 writer,需要大量并发写入的应用也可能要选择别的方案。

公共领域不会改变网络延迟、文件锁和并发写入的物理条件。

[026]

引用 [026]

supports

官方当前适用性文档支撑技术边界;`any purpose` 是权利范围,不是性能、架构适配、支持或无缺陷保证。

定位 Situations Where A Client/Server RDBMS May Work Better and Checklist sections on network access, high-volume websites, and concurrent writers

查看完整证据关系

同样地,任何人可以修改,不等于任何人可以改变官方主线。

任何人可以免费使用,不等于维护者必须免费提供服务。

任何人可以把代码放进产品,不等于上游替下游维护每一份补丁。

这些都不是首页承诺偷偷藏起的“但是”。它们划分的是几种不同责任:使用者决定自己的目的和架构,下游负责自己的构建和修改,维护团队负责官方交付物的质量与权利来源。

真正没有按身份设限的,是拿走这份代码并决定怎样使用它的权利。

再读一次首页那句话。

SQLite source code is in the public-domain and is free to everyone to use for any purpose.

“任何人”,从最初无法控制数据库服务的应用开发者,扩展到手机公司、浏览器团队、操作系统维护者、公司法务,以及根本不知道 SQLite 正在运行的最终用户。

“任何目的”,从读取本地数据,扩展到商业出货、产品内嵌、自行编译、保留下游 patch,甚至出售一件内部使用了 SQLite 的产品。

这两个词没有一次完成。

应用内运行移走了数据库服务。

自写核心代码移走了第三方权利。

公共领域移走了用途许可。

sqlite3.c 移走了构建物的搬运障碍。

浏览器和平台替最终用户完成部署。

Warranty of Title 给特殊法务环境增加证明。

最后,affidavit、canonical source 和那只 firesafe,把限制集中到官方原本的入口。

所以,《sqlite.c》不是一份最短的数据库安装说明。

它是一种分发关系的形状。

维护者不可能认识未来每一位使用者,却提前替他们追问今天每一行代码的来历。下游不必回来领取许可,却要承担自己的编译、修改、测试和产品责任。

公共领域看起来像少了一份许可证。

在 SQLite 这里,它更像一项被持续制造的产品:有人守住原本,有人维护构建物,有人把它带进自己的产品,有人把权利证明锁进保险柜。

然后,一份叫作 sqlite3.c 的文件,继续留在外面。

任何人。

任何目的。

片尾

34:25

你刚刚收听的是《原代码》第六期,《sqlite.c》,副标题“公共领域”。

你可以访问《原代码》的官方网站。网站地址是,原代码三个字的全拼,点 X Y Z。

在那里,你可以查看本期逐字稿、证据引用关系和证据快照,也可以收听往期节目。

感谢你的收听。

我们下期再见。