知识全景图/ 软件工程与系统/ 如何设计一个数据库/ SQL 执行器

如何设计 SQL 执行器

SQL 不是直接执行的字符串,它要被拆成一棵可运行的算子树。

这一页只讲一件事:MiniDB 如何把 SELECT * FROM users WHERE id = 1 变成真正的扫描、过滤、投影和返回结果。
一、第一版失败:只能手写 API
table.scan(row -> row.id == 1) -- 用户想写的是: SELECT * FROM users WHERE id = 1;
问题长出的设计
SQL 字符串不知道结构。Parser 把 SQL 变成 AST。
字段名可能不存在。Binder 绑定表、列、类型。
AST 不能直接跑。Planner 生成逻辑计划。
逻辑计划还没有执行方式。Executor 用算子拉取 tuple。
二、最小执行管线
SQL -> Parser: SelectStmt(table=users, where=id=1) -> Binder: users.id is int -> Planner: Projection(Filter(SeqScan(users))) -> Executor: next() -> tuple
三、第一批算子
算子负责什么
SeqScan从表页按 RID 顺序读 tuple。
IndexScan从 B+Tree 找到 RID,再回表取 tuple。
Filter判断 where 条件。
Projection只输出需要的列。
Insert写 heap page,并维护索引。
Update找到旧 tuple,写新 tuple 或原地更新。
Delete标记删除,并维护索引可见性。
四、执行器必须守住的边界
边界为什么重要
不能绕过事务。Insert / Update / Delete 必须挂到当前 transaction。
不能忘记索引。写表页和写索引页要保持一致。
不能自己决定快慢。执行器跑计划,优化器选计划。
不能返回脏 tuple。读到什么要服从锁或 MVCC 可见性。
五、验收实验
实验通过标准
查询不存在字段。Binder 报错,不进入执行器。
SELECT 指定列。Projection 只返回指定列。
WHERE id = 1。能先用 SeqScan 跑通,再接 IndexScan。
UPDATE 后再 SELECT。结果一致,索引不指向旧值。
事务中 UPDATE 后 ROLLBACK。执行器写入可被事务系统撤销。
一句话:SQL 执行器负责把计划树真正跑起来;它不负责“选最优计划”,但必须保证每个算子读写的数据都符合事务、索引和可见性规则。