LEX
结构体
源码位置:(版本 = MySQL 8.0.37)
LEX 对象当前有以下功能:
- 包含了一些 SQL 命令的通用属性,例如:
sql_command
,数据变更语句语法中是否存在IGNORE
,以及表列表query_tables
- 包含了一些执行的状态变量,例如:
m_exec_started
(当执行开始时设置为true
),plugins
(语句使用的插件列表),insert_update_values_map
(某些INSERT
语句使用的对象映射)等。 - 包含了一些
Sql_cmd
的子类的局部成员,例如:purge_value_list
(用于PURGE
命令),kill_value_list
(用于KILL
命令)。
LEX 对象严格来说是 Sql_cmd
类的一部分,用于那些由 Sql_cmd
类表示的 SQL 命令。对于其余的 SQL 命令,它是一个与当前 THD(Thread Handler Data,线程处理数据)相关联的独立对象。
LEX 对象的生命周期如下:
- LEX 对象可以根据如下任一信息构造:执行的
mem_root
(用于常规语句),Prepared_statement
的mem_root
(用于 prepared statements),SP
的mem_root
(用于存储过程指令),当前mem_root
(用于短生命周期语句)。 - 在使用 LEX 对象之前,调用
lex_start()
函数来初始化它的执行状态部分;它还会调用LEX::reset()
来确保所有成员都被正常初始化 - 使用 LEX 作为工作区来解析(
parse
)和解决(resolve
)语句 - 执行 SQL 语句:在开始执行(实际上是开始优化时),调用
set_exec_started()
。可以调用is_exec_started()
来区分 SQL 命令是在准备阶段还是优化 / 执行阶段。 - 执行完成后调用
clear()
,清除 SQL 命令关联的所有执行状态,其中也会调用LEX::reset_exec_started()
。
下面我们按声明顺序了解 LEX 结构体:
初始化:LEX::lex_start()
在准备和执行每个查询之前,都会先调用 lex_start()
函数。
该函数会创建一个 query_block
和一个 query_block_query_expression
对象。
# sql/sql_lex.cc
bool lex_start(THD *thd) {
DBUG_TRACE;
LEX *lex = thd->lex;
lex->thd = thd;
lex->reset();
// Initialize the cost model to be used for this query
thd->init_cost_model();
const bool status = lex->new_top_level_query();
assert(lex->current_query_block() == nullptr);
lex->m_current_query_block = lex->query_block;
assert(lex->m_IS_table_stats.is_valid() == false);
assert(lex->m_IS_tablespace_stats.is_valid() == false);
return status;
}
在 LEX::reset()
方法中,重置了 LEX
结构体中的大部分变量。
在 THD::init_cost_model()
方法中,初始化了 THD
的 m_cost_model
和 m_cost_model_hypergraph
成员。
# sql/sql_class.cc
void THD::init_cost_model() {
m_cost_model.init(Optimizer::kOriginal);
m_cost_model_hypergraph.init(Optimizer::kHypergraph);
}
在 LEX::new_top_level_query
方法中,创建了 Query_block
对象并赋值给 LEX
的 query_block
成员,创建了 Query_block_query_expression
对象并赋值给 LEX
的 unit
成员。
bool LEX::new_top_level_query() {
DBUG_TRACE;
// Assure that the LEX does not contain any query expression already
assert(unit == nullptr && query_block == nullptr);
// Check for the special situation when using INTO OUTFILE and LOAD DATA.
assert(result == nullptr);
query_block = new_query(nullptr);
if (query_block == nullptr) return true; /* purecov: inspected */
unit = query_block->master_query_expression();
return false;
}
解析:lex_one_token()
在 LEX
结构体中,有成员 m_tok_start
被注释为 “Starting position of the last token parsed”,说明这个成员被用于解析,搜索这个成员的使用位置,发现在 LEX::start_token()
、LEX::restart_token()
、LEX::get_tok_start()
和 LEX::yyLength()
方法中被使用,然后发现这 4 个函数主要在 lex_one_token()
函数或该函数调用的其他方法中被调用,但是在 sql/sql_lex.cc 和 router/src/routing/src/sql_lexer.cc 中有两套 lex_one_token()
解析函数。从引用关系来看,sql_lexer.cc
中的 lex_one_token()
应该是主要解析逻辑。
sql_lexer.cc
中的 lex_one_token()
在 SqlLexer::iterator::Token SqlLexer::iterator::next_token()
中被调用。
执行状态:set_exec_started()
在 set_exec_started()
方法中,将 m_exec_started
成员设置为 true
,而没有进行其他操作。说明 SQL 执行逻辑不在 LEX
结构体中,但在 LEX
结构体中记录了执行状态。
bool is_exec_started() const { return m_exec_started; }
void set_exec_started() { m_exec_started = true; }
此外,还有 m_exec_completed
成员用于存储当前语句是否执行完成:
bool is_exec_completed() const { return m_exec_completed; }
void set_exec_completed() { m_exec_completed = true; }
清除状态:reset_exec_started()
在清除状态的方法中,重置了 m_exec_started
和 m_exec_completed
成员。
void reset_exec_started() {
m_exec_started = false;
m_exec_completed = false;
}
标签:exec,started,sql,lex,源码,MySQL,LEX,query
From: https://blog.csdn.net/Changxing_J/article/details/140284366