目录文档:MySQL 源码|源码剖析文档目录
SQL 语句执行过程
MySQL 中解析 SQL 语句,并构造抽象语法树的逻辑,主要在 parse_sql
函数中,主要执行过程如下:
dispatch_command
(sql/sql_parse.cc):执行一个连接层级的命令。dispatch_sql_command
(sql/sql_parse.cc):从文本字符串中解析一个 SQL 命令,并将生成的抽象语法树(AST)传递给查询执行器。-
parse_sql
(sql/sql_parse.cc):使用提供的解析器状态和对象创建上下文,将一个 SQL 语句转换成一个准备解决的抽象语法树(AST)。THD::sql_parser
(sql/sql_class.cc):首先调用解析器将语句转换为解析树;然后,进一步将解析树转换为抽象语法树(AST),以备解析(resolve)之用。my_sql_parser_parse
:Bison 解析器生成的语法解析入口函数LEX::make_sql_cmd
(sql/sql_lex.cc):使用解析树(parse_tree)实例化一个Sql_cmd
对象,并将其赋值给 Lex。Parse_tree_root::make_cmd
(sql/parse_tree_nodes.h):各个语句根据自身抽象语法树(AST)构造Sql_cmt
对象。
-
mysql_execute_command
(sql/sql_parse.cc):执行保存在thd
和lex->sql_command
中的命令。Sql_cmd::command
(sql/sql_cmd.h):执行Sql_cmd
中的 SQL 语句。
-
dispatch_command
函数
执行一个连接层级的命令。
函数签名如下:
bool dispatch_command(THD *thd, const COM_DATA *com_data,
enum enum_server_command command)
thd
:线程上下文(连接处理器)command
:命令类型com_data
:用于存储生成命令的联合体
其中调用了 dispatch_sql_command
函数:
dispatch_sql_command(thd, &parser_state);
dispatch_sql_command
函数
从文本字符串中解析一个 SQL 命令,并将生成的抽象语法树(AST)传递给查询执行器。
函数签名如下:
void dispatch_sql_command(THD *thd, Parser_state *parser_state)
thd
:线程上下文(当前会话)parser_state
:解析器状态
其中调用了 parse_sql
函数:
err = parse_sql(thd, parser_state, nullptr);
也调用了 mysql_execute_command
函数:
error = mysql_execute_command(thd, true);
parse_sql
函数
使用提供的解析器状态和对象创建上下文,将一个 SQL 语句转换成一个准备解决的抽象语法树(AST)。
函数签名如下:
bool parse_sql(THD *thd, Parser_state *parser_state,
Object_creation_ctx *creation_ctx)
参数含义如下:
thd
:线程上下文parser_state
:解析器状态creation_ctx
:构造对象的上下文
其中调用了 THD::sql_parser()
函数:
const bool mysql_parse_status = thd->sql_parser();
THD::sql_parser()
函数
首先调用解析器将语句转换为解析树;然后,进一步将解析树转换为抽象语法树(AST),以备解析(resolve)之用。
函数逻辑如下,其中调用了 my_sql_parser_parse
函数和 LEX::make_sql_cmd
函数:
bool THD::sql_parser() {
extern int my_sql_parser_parse(class THD * thd,
class Parse_tree_root * *root);
Parse_tree_root *root = nullptr;
if (my_sql_parser_parse(this, &root) || is_error()) {
cleanup_after_parse_error();
return true;
}
if (root != nullptr && lex->make_sql_cmd(root)) {
return true;
}
return false;
}
THD::sql_parser
函数在其他调用位置如下:
parse
(sql/item_strfunc.cc):解析一个字符串并填充 Token 缓冲区。reparse_common_table_expr
(sql/sql_parse.cc):从子查询的文本中生成一个PT_subquery
对象
my_sql_parser_parse
函数
Bison 解析器生成的语法解析入口函数,详见 MySQL 源码|82 - 词法解析和语法解析的入口逻辑。
LEX::make_sql_cmd
函数
使用解析树(parse_tree)实例化一个 Sql_cmd
对象,并将其赋值给 Lex。
函数逻辑如下,其中调用了 Parse_tree_root::make_cmd
函数:
bool LEX::make_sql_cmd(Parse_tree_root *parse_tree) {
if (!will_contextualize) return false;
m_sql_cmd = parse_tree->make_cmd(thd);
if (m_sql_cmd == nullptr) return true;
assert(m_sql_cmd->sql_command_code() == sql_command);
return false;
}
Parse_tree_root::make_cmd
函数
Parse_tree_root
是语法解析中各类语句的根节点的基类,其中定义了 make_cmd
函数,并由各个语句子类去实现。因此,Parse_tree_root::make_cmd
函数实际上就是各个语句根据自身抽象语法树(AST)构造 Sql_cmt
对象的过程。
mysql_execute_command
函数
执行保存在 thd
和 lex->sql_command
中的命令。
函数签名如下:
int mysql_execute_command(THD *thd, bool first_level)
thd
:线程上下文first_level
:调用mysql_execute_command()
的是一个顶级查询还是子查询。在顶级查询时,first_level
的值为true
;在子查询时,first_level
(递归调用mysql_execute_command()
)将为false
。
在 mysql_execute_command
函数中,不同类型的 SQL 语句有不同的处理方法,但 DQL、DML 等语法均调用了 Sql_cmd
的子类的 execute
成员函数:
lex->m_sql_cmd->execute(thd);
Sql_cmd::command
函数
执行 Sql_cmd
中的 SQL 语句。
函数签名如下:
virtual bool execute(THD *thd) = 0;
thd
:线程上下文