MySQL词法语法解析

一、词法解析: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;

  1. Token序列
    [SELECT, id, FROM, (, SELECT, *, FROM, t1, ), AS, sub, WHERE, id, >, 10, ;]

  2. Bison推导路径

    plaintext 复制代码
    query → select_stmt ;
    select_stmt → SELECT select_expr_list FROM table_reference WHERE condition
    table_reference → ( select_stmt ) AS IDENTIFIER
  3. 生成AST

    plaintext 复制代码
    SELECT
    ├─ 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;时,语法树构建过程如下:

  1. SELECT列表
    • id+1 → 构建Item_func_plus节点,左操作数为Item_field("id"),右操作数为Item_int(1)
  2. WHERE条件
    • age > 20 → 构建Item_func_gt节点,左操作数为Item_field("age"),右操作数为Item_int(20)
  3. 元数据绑定
    • 调用Item_field::fix_fields()idage绑定到users表的列元数据(Field对象)。

3. 协同工作流程

以查询SELECT SUM(salary) FROM employees WHERE department='IT';为例:

  1. 词法语法解析
    • THD->LEX记录表employees、WHERE条件department='IT'
  2. 语法树构建
    • SUM(salary) → 生成Item_sum_sum(Item_field("salary"))
    • department='IT' → 生成Item_func_eq(Item_field("department"), Item_string("IT"))
  3. 执行阶段计算
    • 遍历employees表,对每一行调用Item_func_eq::val_bool()过滤出department='IT'的行。
    • 对过滤后的行调用Item_sum_sum::val_real()累加salary值。
相关推荐
就改了1 小时前
mysql进阶——数据类型一篇详解
数据库·sql
齐 飞5 小时前
MySQL中 IN 到底走不走索引?
数据库·mysql
weixin_307779136 小时前
Visual Studio 2022和C++实现带多组标签的Snowflake SQL查询批量数据导出程序
开发语言·c++·数据仓库·sql·云计算
Excuse_lighttime6 小时前
MYSQL视图
数据库·sql·mysql
XU磊2606 小时前
《Java SQL 操作指南:深入理解 Statement 用法与优化》
java·数据库·sql
Kaede68 小时前
如何在宝塔mysql修改掉3306端口
linux·服务器·数据库·mysql
小林熬夜学编程8 小时前
【MySQL】第十八弹---数据库管理基础:视图操作与用户权限管理指南
linux·运维·服务器·数据库·mysql
野生派蒙10 小时前
MySQL 安装配置(完整教程)
数据库·mysql
溟洵10 小时前
【MySQL】事务是个啥?(一文带你了解 为什么要有事务、事务在mysql中的出现情况以及事务的四大特性)
linux·运维·数据库·后端·mysql
m0_5485147710 小时前
五款MySQL 可视化客户端软件,你选择哪一种?
数据库·mysql