一、词法解析:SQL文本的"原子化"过程
1.1 MySQL词法解析核心原理
MySQL的词法解析器(Lexer)采用自研实现 而非Flex工具,主要出于对性能的极致追求。其核心任务是将SQL字符串拆解为 词法记号(Token) ,具体流程如下:
c
// 伪代码示例:MySQL词法解析核心逻辑(简化版)
while (sql_str未结束) {
char c = 获取当前字符;
if (c是字母或下划线) {
提取完整标识符(如表名users) → 生成IDENTIFIER Token;
} else if (c是数字) {
提取数值 → 生成INTEGER/FLOAT Token;
} else if (c是引号) {
提取字符串 → 生成STRING Token;
} else if (匹配到关键字如SELECT) {
生成KEYWORD Token;
} else {
报错(如非法符号@);
}
}
设计特点:
- 高速缓存机制:预编译高频Token加速匹配(如SELECT/WHERE等)
- 状态机驱动 :通过
lex_one_token()
函数实现多模式切换(常规模式、注释模式、字符串模式等) - 原生性能优化:相比Flex生成的通用解析器,MySQL词法分析代码体积减少30%,解析速度提升25%
1.2 词法记号(Token)分类
Token类型 | 示例 | 正则规则(简化) |
---|---|---|
关键字 | SELECT, WHERE | 精确字符串匹配 |
标识符 | users, id | [a-zA-Z_][a-zA-Z0-9_]* |
数值 | 123, 45.67 | \d+(.\d+)? |
字符串 | 'hello' | '([^']*)' |
操作符 | =, >, * | = |
分隔符 | ;, (, ) | 精确符号匹配 |
二、语法解析:从Token流到抽象语法树(AST)
2.1 Bison与上下文无关文法(CFG)
MySQL采用Bison工具生成LALR(1)解析器,其核心是基于上下文无关文法的规则推导。以SELECT子查询为例:
bison
/* sql/sql_yacc.yy 中简化规则 */
select_stmt:
SELECT select_expr_list
FROM table_reference_list
[WHERE condition]
[GROUP BY group_expr]
;
table_reference:
IDENTIFIER /* 普通表 */
| '(' select_stmt ')' AS IDENTIFIER /* 子查询 */
;
condition:
expr IN '(' select_stmt ')' /* IN子查询 */
| EXISTS '(' select_stmt ')' /* EXISTS子查询 */
;
CFG关键概念:
- 终结符:Token类型(如SELECT/IDENTIFIER)
- 非终结符:语法结构(如select_stmt/table_reference)
- 产生式:定义非终结符的展开规则
2.2 语法树构建过程
示例解析 :SELECT id FROM (SELECT * FROM t1) AS sub WHERE id > 10;
-
Token序列 :
[SELECT, id, FROM, (, SELECT, *, FROM, t1, ), AS, sub, WHERE, id, >, 10, ;]
-
Bison推导路径:
plaintextquery → select_stmt ; select_stmt → SELECT select_expr_list FROM table_reference WHERE condition table_reference → ( select_stmt ) AS IDENTIFIER
-
生成AST:
plaintextSELECT ├─ Projection: id ├─ FROM │ └─ Subquery │ ├─ SELECT * │ └─ Alias: sub └─ WHERE └─ Condition: id > 10
2.3 关键数据结构
1. THD->LEX:会话级解析状态容器
核心作用 :
作为MySQL线程描述符(THD
)的关键成员,LEX
存储了当前SQL语句解析过程中的上下文状态,包括表引用、条件表达式、列列表等中间信息。这些数据在查询优化和执行阶段被复用,避免了重复解析。
结构组成(关键字段):
SELECT_LEX_UNIT
: 管理复杂查询结构(如UNION
操作、子查询嵌套)。TABLE_LIST
: 链表结构存储所有涉及的表/视图(含别名和连接类型)。where_cond
: 指向WHERE条件的表达式树(Item
派生类实例)。current_select
: 指向当前正在处理的SELECT
块(用于嵌套查询)。
实例分析 :
解析如下查询时:
sql
SELECT t1.id, t2.name
FROM orders AS t1
JOIN customers t2 ON t1.cid = t2.id
WHERE t1.amount > 1000;
THD->LEX
会记录:
- 表列表 :
orders
(别名为t1
),customers
(别名为t2
)。 - 连接条件 :
t1.cid = t2.id
→ 转换为Item_func_eq
表达式树。 - WHERE条件 :
t1.amount > 1000
→ 转换为Item_func_gt
表达式树。
2. Item类:语法树的抽象与计算引擎
类层级设计 :
Item
是所有表达式节点的基类,通过派生类实现不同类型的数据处理和计算逻辑:
派生类 | 功能 | 典型场景 |
---|---|---|
Item_field |
表示对表中某列的引用(如users.id ) |
SELECT id FROM users |
Item_int |
整型常量(如WHERE age=25 ) |
INSERT INTO t VALUES (100) |
Item_float |
浮点型常量(如3.14 ) |
UPDATE prices SET tax=price*0.05 |
Item_string |
字符串常量(如'MySQL' ) |
WHERE name='Alice' |
Item_func |
函数调用(如SUM(salary) 、CONCAT(first_name, last_name) ) |
SELECT COUNT(*) FROM orders |
Item_cond |
逻辑条件(AND/OR 组合) |
WHERE age>18 AND status='active' |
核心方法解析:
cpp
class Item {
public:
// 数值计算(返回double类型)
virtual double val_real() {
// 示例:Item_int::val_real() 返回整数值的double形式
}
// 字符串计算(返回String对象指针)
virtual String *val_str(String *str) {
// 示例:Item_string::val_str() 返回字符串缓冲
}
// 布尔判断(返回true/false)
virtual bool val_bool() {
// 示例:Item_func_gt::val_bool() 比较左右操作数的大小
}
// 元数据绑定(解析阶段调用)
virtual bool fix_fields(THD *thd, Item **ref) {
// 示例:Item_field绑定到实际表的Field对象
}
};
实例分析 :
解析查询SELECT id+1 FROM users WHERE age > 20;
时,语法树构建过程如下:
- SELECT列表 :
id+1
→ 构建Item_func_plus
节点,左操作数为Item_field("id")
,右操作数为Item_int(1)
。
- WHERE条件 :
age > 20
→ 构建Item_func_gt
节点,左操作数为Item_field("age")
,右操作数为Item_int(20)
。
- 元数据绑定 :
- 调用
Item_field::fix_fields()
将id
和age
绑定到users
表的列元数据(Field
对象)。
- 调用
3. 协同工作流程
以查询SELECT SUM(salary) FROM employees WHERE department='IT';
为例:
- 词法语法解析 :
THD->LEX
记录表employees
、WHERE条件department='IT'
。
- 语法树构建 :
SUM(salary)
→ 生成Item_sum_sum(Item_field("salary"))
。department='IT'
→ 生成Item_func_eq(Item_field("department"), Item_string("IT"))
。
- 执行阶段计算 :
- 遍历
employees
表,对每一行调用Item_func_eq::val_bool()
过滤出department='IT'
的行。 - 对过滤后的行调用
Item_sum_sum::val_real()
累加salary
值。
- 遍历