SQLite 前端内部:分词器、解析器和代码生成器
Source: Dev.to
你好,我是Maneshwar。 我正在开发 FreeDevTools online —— 一个免费、开源的中心,汇集所有开发工具、技巧代码和 TLDR,帮助开发者无需无尽搜索即可找到所需。
SQLite 前端深度解析
在上一篇文章中,我审视了 SQLite 的整体架构以及它如何将 SQL 编译与执行清晰地分离。那一层面的概览把 前端 介绍为将 SQL 文本转换为可执行字节码的组件。
今天的学习将更深入地探讨该前端管线:SQL 语句是如何被拆解、理解,最终转换为 SQLite 虚拟机可以执行的字节码程序。
标记器
当应用程序提交 SQL 语句或 SQLite 命令时,第一个看到原始输入的组件是标记器。
它的工作直截了当却至关重要:
- 扫描输入字符串。
- 将其拆分为单独的标记。
- 将这些标记一次一个地送给解析器。
标记包括
- SQL 关键字(
SELECT、INSERT、WHERE) - 标识符(表名和列名)
- 字面量(数字、字符串)
- 运算符和标点
标记器的实现位于 tokenize.c 源文件中。
一个不寻常的控制流选择
在许多使用 YACC 或 BISON 构建的编译器工具链中,解析器调用标记器来请求下一个标记。SQLite 颠倒了这种关系:标记器调用解析器。
Richard Hipp 对两种方式都做了实验,发现让标记器驱动解析器可以得到更简洁、更易维护的代码。这种反转简化了状态处理,并且更好地与 SQLite 的执行模型集成。
解析器
一旦标记生成完毕,解析器会根据上下文为它们赋予 意义。SQLite 的解析器是使用 Lemon 生成的,Lemon 是专为 SQLite 设计的 LALR(1) 解析器生成器。
为什么选择 Lemon?
- 可重入 的解析器。
- 线程安全 的解析器。
- 生成的代码 抗内存泄漏。
这些特性与 SQLite 的嵌入式和多线程使用场景完美契合。
语法定义与生成代码
SQLite 的语法在 parse.y 文件中定义,内容包括:
- SQL 语法规则。
- SQLite 特有的命令和扩展。
基于该语法,Lemon 生成:
| 文件 | 用途 |
|---|---|
parse.c | 解析器实现 |
parse.h | 标记类型的数值代码 |
解析器:
- 验证 SQL 语法。
- 构建解析树。
- 识别诸如表达式、子句和语句等语义结构。
关于 Lemon 可用性的说明
与 YACC 或 BISON 不同,Lemon 通常不会预装在开发系统上。SQLite 将 Lemon 的完整源代码(lemon.c)放在其 tool 目录中,并附带文档。这样可以确保 SQLite 随时能够在没有外部依赖的情况下重新生成其解析器——又一次体现了其自给自足的理念。
代码生成器
在解析器消耗完所有标记并组装出完整的解析树后,控制权转交给 代码生成器。
它的职责:
- 遍历解析树。
- 生成等价的 SQLite 字节码程序。
- 确保程序产生的效果完全符合 SQL 语句的描述。
真正逻辑所在
SQLite 的代码生成逻辑分布在多个源文件中,每个文件负责处理特定类别的 SQL 语句或结构:
| 文件 | 责任 |
|---|---|
expr.c | 表达式处理与字节码生成 |
select.c | SELECT 语句的编译 |
insert.c | INSERT 语句的编译 |
update.c | UPDATE 语句的编译 |
delete.c | DELETE 语句的编译 |
trigger.c | 触发器相关的字节码生成 |
sqlite3.c | 核心引擎入口与整体调度 |
(后续章节将继续深入每个文件的具体实现细节。)
| 表达式和计算 |
| where.c | WHERE 子句逻辑,适用于 SELECT、UPDATE、DELETE |
| select.c | SELECT 语句 |
| insert.cdelete.cupdate.c | 数据修改语句 |
| trigger.c | 触发器执行逻辑 |
| attach.c | 数据库附加处理 |
| vacuum.c | 数据库重组 |
| pragma.c | PRAGMA 命令 |
| build.c | 模式及其他杂项语句 |
| auth.c | 通过 sqlite3_set_authorizer 进行授权 |
特定语句的文件将公共逻辑(例如表达式处理或谓词求值)委托给共享模块,如 expr.c 和 where.c。这种模块化保持了代码库的组织性,并强化了 SQLite 的架构清晰度。
从 SQL 文本到字节码
在前端管线的末端:
- SQL 文本已被标记化。
- 语法已被验证。
- 语义已被解析。
- 已生成优化的字节码程序。
所有这些工作都在 sqlite3_prepare API 调用内部完成,尽管对应用程序是透明的。应用程序收到的是一个 已准备好的语句句柄——但在该句柄背后是一个已完全编译好的、准备执行的程序。
结束语
今天的学习表明,SQLite 在 API 层面的简洁性得益于 精心设计的编译流水线。具体表现为:
- 让分词器驱动解析器,
- 使用自定义的安全解析器生成器(Lemon),以及
- 将代码生成组织成清晰、模块化的组件,
SQLite 实现了既强大又易于维护的前端。
我与 SQLite 相关的实验和动手实践可在此处找到:lovestaco/sqlite
参考文献
-
SQLite Database System: Design and Implementation – Sibsankar Haldar. 在 Google Books 上查看
-

👉 查看: FreeDevTools
欢迎任何反馈或贡献者!它是在线的、开源的,随时可供任何人使用。
⭐ 在 GitHub 上给它加星: freedevtools
