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值。
相关推荐
陌年微凉&2 小时前
MySQL与Canal、RabbitMQ集成指南
数据库·mysql·缓存·rabbitmq
歪歪歪。。4 小时前
黑马程序员javaweb案例---初识管理系统(spring-boot)
java·spring boot·sql·tomcat·maven
堕落年代5 小时前
使用MySQL的Binlog来同步数据到ES当中
mysql·elasticsearch·adb
kfepiza5 小时前
Fedora41安装MySQL8.4.4
linux·mysql
m0_748235076 小时前
Python高级之操作Mysql
python·mysql·adb
s听风忆雪6 小时前
navicat16 升级到 navicat17 之后原来的连接找不到了 mac用户
mysql·macos·navicat
2302_799525746 小时前
【MySQL】MySQL程序解析
数据库·mysql
Lzg丶7 小时前
《MySQL数据库从零搭建到高效管理|库的基本操作》
数据库·mysql
Code额7 小时前
MySQL 的索引机制
数据库·mysql