SQLite 前端内部:分词器、解析器和代码生成器

发布: (2026年1月11日 GMT+8 02:26)
7 min read
原文: Dev.to

Source: Dev.to

你好,我是Maneshwar。 我正在开发 FreeDevTools online —— 一个免费、开源的中心,汇集所有开发工具、技巧代码和 TLDR,帮助开发者无需无尽搜索即可找到所需。

SQLite 前端深度解析

在上一篇文章中,我审视了 SQLite 的整体架构以及它如何将 SQL 编译与执行清晰地分离。那一层面的概览把 前端 介绍为将 SQL 文本转换为可执行字节码的组件。

今天的学习将更深入地探讨该前端管线:SQL 语句是如何被拆解、理解,最终转换为 SQLite 虚拟机可以执行的字节码程序

Image

标记器

当应用程序提交 SQL 语句或 SQLite 命令时,第一个看到原始输入的组件是标记器

它的工作直截了当却至关重要:

  1. 扫描输入字符串。
  2. 将其拆分为单独的标记。
  3. 将这些标记一次一个地送给解析器。

标记包括

  • SQL 关键字(SELECTINSERTWHERE
  • 标识符(表名和列名)
  • 字面量(数字、字符串)
  • 运算符和标点

标记器的实现位于 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.cSELECT 语句的编译
insert.cINSERT 语句的编译
update.cUPDATE 语句的编译
delete.cDELETE 语句的编译
trigger.c触发器相关的字节码生成
sqlite3.c核心引擎入口与整体调度

(后续章节将继续深入每个文件的具体实现细节。)

| 表达式和计算 | | where.c | WHERE 子句逻辑,适用于 SELECTUPDATEDELETE | | select.c | SELECT 语句 | | insert.c
delete.c
update.c | 数据修改语句 | | trigger.c | 触发器执行逻辑 | | attach.c | 数据库附加处理 | | vacuum.c | 数据库重组 | | pragma.c | PRAGMA 命令 | | build.c | 模式及其他杂项语句 | | auth.c | 通过 sqlite3_set_authorizer 进行授权 |

特定语句的文件将公共逻辑(例如表达式处理或谓词求值)委托给共享模块,如 expr.cwhere.c。这种模块化保持了代码库的组织性,并强化了 SQLite 的架构清晰度。

从 SQL 文本到字节码

在前端管线的末端:

  1. SQL 文本已被标记化。
  2. 语法已被验证。
  3. 语义已被解析。
  4. 已生成优化的字节码程序。

所有这些工作都在 sqlite3_prepare API 调用内部完成,尽管对应用程序是透明的。应用程序收到的是一个 已准备好的语句句柄——但在该句柄背后是一个已完全编译好的、准备执行的程序。

结束语

今天的学习表明,SQLite 在 API 层面的简洁性得益于 精心设计的编译流水线。具体表现为:

  • 让分词器驱动解析器,
  • 使用自定义的安全解析器生成器(Lemon),以及
  • 将代码生成组织成清晰、模块化的组件,

SQLite 实现了既强大又易于维护的前端。

我与 SQLite 相关的实验和动手实践可在此处找到:lovestaco/sqlite

参考文献

欢迎任何反馈或贡献者!它是在线的、开源的,随时可供任何人使用。

在 GitHub 上给它加星: freedevtools

Back to Blog

相关文章

阅读更多 »

SQLite 中的系统事务到用户事务

用户事务:逃离自动提交 默认情况下,SQLite 运行在 autocommit 模式。每个非 SELECT 语句都会被包装在它自己的事务中:!Autocommit tr...