MySQL-Parser

MySQL-Parser

引言

本想续上节,介绍Leveldb引擎的插入操作,但涉及许多Parser相关的内容。
就先分析Parser,前面章节提到SQL解析,只是简单介绍一下,这里将介绍核心的类对象和流程。

sql_yacc.cc

sql_yacc.cc与sql_yacc.h文件的产生,如下所示:

/usr/bin/bison --name-prefix=MYSQL --yacc --output=./sql_yacc.cc 
    --defines=./sql_yacc.h ./sql_yacc.yy

关键点就是前缀,通过宏将内部函数与外部声明函数建立关联

#define yyparse         MYSQLparse
#define yylex           MYSQLlex
#define yyerror         MYSQLerror
#define yydebug         MYSQLdebug
#define yynerrs         MYSQLnerrs

extern int MYSQLparse(class THD *thd);
// 实现sql_parser.cc中函数声明

sql_yacc.yy文件中如下两行表示yyparse和yylex函数的参数。

%parse-param { class THD *YYTHD }
%lex-param { class THD *YYTHD }

核心对象

class Parse_tree_node{};
/**
    parse_tree_node_base.h
    解析树节点的基类,虚类
    单纯记录节点是否语境化
    Item类即是其友元类又是其派生类,Item通过友元直接访问其内部变量
    并且Item类将其成员函数私有化,Item类通过itemize函数实现语境化
    就设计而言个人感觉很怪[^_^],派生类是基类的友元
*/

class Item : public Parse_tree_node{};
/**
    item.h
    Item类是解析树的基本元素,抽象类
    Expression对象的基类(https://dev.mysql.com/doc/refman/5.7/en/expressions.html)
    功能很复杂,感觉其包含许多子类的功能,这样好处是直接可调用基类中的函数
    若将功能推迟到派生类,处理时需要将其转换为派生类才能调用函数
    定义许多bool值成员,通过bool值表示expression的某些特性
*/

class Table_ident :public Sql_alloc{};
/*  sql_class.h
    解析时SQL语句中的表对象
    add_table_to_list()函数利用Table_ident构建TABLE_LIST对象,
    并将其插入到lex->query_tables全局链表中
*/

class PT_statement : public Parse_tree_node{};
/**
    parse_tree_nodes.h
    解析时SQL语句对象的基类,抽象类
    只有一个纯虚函数成员,make_cmd
    返回一个运行时使用的Sql_cmd对象,这样parser与runtime就关联起来
*/

class Sql_cmd : public Sql_alloc{};
/**
    sql_cmd.h
    执行时SQL语句对象的基类,抽象类
    execute纯虚函数成员,实现语句执行操作
    由具体的派生类实现
*/

class Query_tables_list{};
/**
    sql_lex.h
    SQL语句中使用到的表列表,以及open和lock相关信息
*/

struct LEX: public Query_tables_list{};
/**
    sql_lex.h
    LEX分析的状态以及结果信息,包含解析后的所有信息
*/

class st_select_lex: public Sql_alloc{};
/**
    sql_lex.h
    SQL语句中的查询块,或规范的查询
*/

class st_select_lex_unit: public Sql_alloc{};
/**
    sql_lex.h
    查询表达式,由一个或多个st_select_lex构成
*/

struct TABLE_LIST{};
/**
    table.h
    from子句中的所有引用的表构成的LIST对象
*/

struct TABLE{};
/**
    table.h
    具体的表对象,与底层存储引擎交互
    调用open_tables_for_query函数实例化
*/

struct TABLE_SHARE{};
/**
    table.h
    TABLE对象共享的实例,每个表只存在一个实例,表元数据信息
*/

CREATE TABLE分析

CREATE TABLE parse_t(
    id INT NOT NULL,
    cv1 VARCHAR(20) DEFAULT "",
    tt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    dayweek SMALLINT,
    cv2 INT,
    PRIMARY KEY(id)
) ENGINE=innodb DEFAULT CHARSET=utf8mb4;

解析核心是将SQL语句转换PT_statement类对象。
CREATE TABLE比较简单,未定义其对应的PT_statement类。
例子中核心两点是:

  • create_field_list,字段定义列表
  • create_table_options,表选项

//解析过程即实现语境的操作中,其中关键流程如下:

lex->sql_command= SQLCOM_CREATE_TABLE;
/**
    sql_yacc.yy:2262
    确定command类型
*/

st_select_lex::add_table_to_list();
/**
    sql_yacc.yy:2263
    1、以Table_ident对象构造TABLE_LIST
    2、将新对象插入到thd->lex->select_lex->table_list链表中
*/  

//sql_yacc.yy:6259 键匹配,构建Key实例将其存入lex->alter_info.key_list链表中


/**
    sql_yacc.yy:6467 字段数据类型匹配,
    sql_yacc.yy:6880 字段属性匹配
*/
add_field_to_list();
/**
    sql_yacc.yy:6344
    1、检查字段名称长度是否合法
    2、确定字段是否是KEY,若是构建KEY,存入lex->alter_info.key_list
    本例中主键单独行定义,走sql_yacc.yy:6259匹配
    3、检查字段类型与默认值以及ON UPDATE是否匹配
    4、构建Create_field,存入lex->alter_info.create_list
*/

class Key_part_spec :public Sql_alloc{};    //sql_class.h
//本例中未用到,由多字段组成的键中的每个字段都是Key_part_spec
class Key{};    //sql_class.h
//表中定义的键,
class Create_field :public Sql_alloc{}; //field.h
//表中字段对象,包含字段所有信息
class Alter_info{}; 
/**
    sql_alter.h
    描述表信息,CREATE TABLE与ALTER TABLE共用
    lex->alter_info即为Alter_info实例
*/

INSERT分析

MySQL支持三类Insert语法:

  • insert into values
  • insert into set
  • insert into select

分析例句如下:

INSERT INTO parse_t(id, cv1, tt, dayweek, cv2)
    values (DAY(NOW()), 'simple e   xample', NOW(), DAYOFWEEK(NOW()), 12),
    (2, 'simple example2', NOW(), HOUR(NOW()), (20 - 14));

“`C++
/**
sql_yacc.yy:11344
insert_stmt三种匹配模式对应上述三类语法。
核心是匹配中insert_from_constructor
*/

class PT_insert_values_list : public Parse_tree_node{};
/**
VALUES中行解析为双重链表,存入PT_insert_values_list中many_values字段中
*/

class PT_insert : public PT_statement{};
/**
sql_yacc.yy:11354
语句解析完成,实例化PT_insert对象
*/

/**
sql_yacc.yy:12294
调用PT_insert::make_cmd函数,实例化Sql_cmd_insert对象
*/

//PT_insert::make_cmd中语境处理非常关键

PT_insert::contextualize();
//语境化处理将Parser与运行时对象关联起来
//主要功能:

// 1、为lex->sql_command赋值表示CMD类型
lex->sql_command = SQLCOM_INSERT;

//2、构造TABLE_LIST对象,将其插入lex->select_lex->table_list链表中
pc->select->add_table_to_list(pc->thd, table_ident, NULL,
TL_OPTION_UPDATING,
yyps->m_lock_type,
yyps->m_mdl_type,
NULL,
opt_use_partition);

//3、设置表的锁类型,open_tables()时用到
pc->select->set_lock_for_tables(lock_option);

//4、column_list语境化
column_list->contextualize(pc);

/**
5、row_value_list语境化
着重分析DAYOFWEEK(NOW())的解析与语境
*/
row_value_list->contextualize(pc)

/**
由于weekofday函数名并不是SQL中标记单词,匹配function_call_generic sql_yacc.yy:9986
构造PTI_function_call_generic_ident_sys实例 sql_yacc.yy:9989
其参数NOW(),匹配sql_yacc.yy:10045,构造PTI_udf_expr实例

PTI_function_call_generic_ident_sys语境化:
1、检查名称字符是否合法,长度
    sp_check_name(&ident);
2、查询其是否是内置函数,若存在返回其建造者
    find_native_function_builder(thd, ident);
3、建造者构建Item_func_weekday对象
    *res= builder->create_func(thd, ident, opt_udf_expr_list);
4、Item_func_weekday语境化处理,调用Item_func::itemize函数
    主要是对参数进行语境化处理

*/

语境化中Item::fixed成员被处理,若值需进一步处理则为0,表示未处理。若是数字或字符串则为1。
fill_record()函数中才处理得到真实值。

总结

至此,Parser部分分析结束,介绍其涉及的类型以及其中关键的的步骤。
核心就两个点,yacc匹配处理和语境化处理。
parser部分设计挺优雅,理清关系流程后,分析定位都比较简单。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
binlog-parser是一种用于解析和分析MySQL二进制日志(binlog)的工具或程序。MySQL的binlog是MySQL服务器用来记录对数据库进行的更改的一种日志格式,它包含了对数据库的增、删、改操作的详细记录。 binlog-parser具有以下功能和用途: 1. 解析二进制日志:binlog-parser可以将二进制日志转换为易于理解的文本格式,方便开发人员或管理员阅读和分析其中的操作详情。 2. 数据恢复:当数据遭受意外删除、修改或误操作时,binlog-parser可以帮助恢复或还原数据库中受影响的数据,通过分析binlog中的记录并执行相应的SQL语句来重新执行对数据库的更改操作。 3. 数据同步:binlog-parser可以将binlog中的操作转换为可执行的SQL语句,并应用到其他MySQL服务器上,实现数据的实时同步。 4. 审计与追踪:通过分析binlog,binlog-parser可以帮助进行数据库操作的审计和追踪,记录每次对数据库的更改操作,便于后续的安全审计和故障排查。 5. 数据分析与统计:binlog-parser可以提取binlog中的统计信息,如数据修改的频率、数据量的变化等,帮助开发人员进行数据分析和性能优化。 总之,binlog-parser是一种有助于解析、分析和管理MySQL二进制日志的工具或程序,它在数据恢复、数据同步、审计与追踪以及数据分析等方面发挥重要作用,对于MySQL数据库的管理和维护是非常有帮助的。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值