MySQL 源码|7 - LEX 结构体

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_statementmem_root(用于 prepared statements),SPmem_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() 方法中,初始化了 THDm_cost_modelm_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 对象并赋值给 LEXquery_block 成员,创建了 Query_block_query_expression 对象并赋值给 LEXunit 成员。

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.ccrouter/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_startedm_exec_completed 成员。

  void reset_exec_started() {
    m_exec_started = false;
    m_exec_completed = false;
  }
  • 10
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

长行

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值