首页 > 数据库 >MySQL 源码解读之-语法解析(四)

MySQL 源码解读之-语法解析(四)

时间:2022-11-19 13:33:45浏览次数:40  
标签:语句 engine const cmd 语法 源码 bool MySQL secondary

MySQL 源码解读之-语法解析(四)

在上篇文章中,我们分析了一条 sql 语句 select * from bank; 警告bison 语法解析器(MYSQLparser 函数)生成的AST 树的结构,如下图所示:

mysql 需要对这个AST 做进一步的处理,调用 LEX::make_sql_cmd 函数将当前的 AST树实例化Sql_cmd对象并将其分配给Lex。

Sql_cmd数据结构

Sql_cmd 类是 sql 命令的表现形式,此类是解析器和运行时之间的接口。解析器构建相应的Sql_cmd派生类,以表示已解析树中的Sql语句。Sql_cmd派生类中的execute()方法包含运行时实现。请注意,此接口用于最近实现的SQL语句,旧语句的代码倾向于加载具有更多属性的LEX结构。

通过对Sql_cmd进行子类化来实现新语句,因为这提高了代码的模块性(参见dispatch_command()中的“大开关”),并减少了LEX结构的总大小(因此节省了存储程序中的内存)。Sql_cmd派生类的推荐名称为Sql_cmd_<derived>。

class Sql_cmd {
 public:
  virtual enum_sql_command sql_command_code() const = 0;      // 返回当前语句的命令码,如 SQLCOM_SELECT

  // 如果对象表示可准备语句,即用PREPARE语句准备并用EXECUTE语句执行的查询,则返回true。对于直接执行的常规语句(非可准备语句),返回False。如果语句是存储过程的一部分,则也是false
  bool needs_explicit_preparation() const {
    return m_owner != nullptr && !m_part_of_sp;
  }

  // 如果语句是正则的,则返回true. 既不是 prepare 语句也不是存储过程的一部分
  bool is_regular() const { return m_owner == nullptr && !m_part_of_sp; }
  
  // 判断一个语句是是否是 prepare 语句
  bool is_prepared() const { return m_prepared; }

  // prepare 这个语句
  virtual bool prepare(THD *) {
    assert(!is_prepared());
    set_prepared();
    return false;
  }

  // 执行这个语句
  virtual bool execute(THD *thd) = 0;

  // Command-specific reinitialization before execution of prepared statement
  virtual void cleanup(THD *) { m_secondary_engine = nullptr; }

  // 设置所属的prepare语句
  void set_owner(Prepared_statement *stmt) {
    assert(!m_part_of_sp);
    m_owner = stmt;
  }

  // 设置所属的prepare语句
  Prepared_statement *owner() const { return m_owner; }

  // 将语句标记为过程的一部分。这样的语句可以执行多次,第一次execute()调用也会准备它
  void set_as_part_of_sp() {
    assert(!m_part_of_sp && m_owner == nullptr);
    m_part_of_sp = true;
  }
  // 判断语句是否是存储过程的一部分
  bool is_part_of_sp() const { return m_part_of_sp; }

  // 判断一个语句是否DML
  virtual bool is_dml() const { return false; }

  // 如果实现为单表执行计划,则返回true,仅限DML语句
  virtual bool is_single_table_plan() const {
    assert(is_dml());
    return false;
  }

  virtual bool accept(THD *, Select_lex_visitor *) { return false; }

  virtual const MYSQL_LEX_CSTRING *eligible_secondary_storage_engine() const {
    return nullptr;
  }

  void disable_secondary_storage_engine() {
    assert(m_secondary_engine == nullptr);
    m_secondary_engine_enabled = false;
  }

  // 此语句是否禁用了辅助存储引擎的使用?
  bool secondary_storage_engine_disabled() const {
    return !m_secondary_engine_enabled;
  }

  void use_secondary_storage_engine(const handlerton *hton) {
    assert(m_secondary_engine_enabled);
    m_secondary_engine = hton;
  }

  bool using_secondary_storage_engine() const {
    return m_secondary_engine != nullptr;
  }

  // 获取用于执行此语句的辅助引擎的handlerton,如果未使用辅助引擎,则获取nullptr
  const handlerton *secondary_engine() const { return m_secondary_engine; }

  void set_optional_transform_prepared(bool value) {
    m_prepared_with_optional_transform = value;
  }

  bool is_optional_transform_prepared() {
    return m_prepared_with_optional_transform;
  }

 protected:
  Sql_cmd() : m_owner(nullptr), m_part_of_sp(false), m_prepared(false) {}

  virtual ~Sql_cmd() {
    // Sql_cmd对象在thd->mem_root中分配。在MySQL中,从未调用C++析构函数,而是简单地销毁底层MEM_ROOT。不要依赖析构函数进行任何清理。
    assert(false);
  }

  /// 设置语句为 prepare 语句
  void set_prepared() { m_prepared = true; }

 private:
  Prepared_statement *m_owner;  // prepare 语句,如果不是 prepate 值为 NULL
  bool m_part_of_sp;            // 是否是存储过程的一部分
  bool m_prepared;              // 已经被 prepare 的语句为 true

  // 指示辅助存储引擎是否可用于此语句。如果为false,则不会考虑使用辅助存储引擎来执行此语句。
  bool m_secondary_engine_enabled{true};

  // 跟踪语句是否准备了可选转换。
  bool m_prepared_with_optional_transform{false};

  // 用于执行此语句的辅助存储引擎(如果有),如果使用主引擎,则为nullptr。此属性在每次执行开始时重置。
  const handlerton *m_secondary_engine{nullptr};
};

源码调试

我们此次把断点打到 LEX::make_sql_cmd 函数,然后执行 select * from bank; 如图所示,已经命中了该断点:

我们执行 s 进去make_sql_cmd 函数进行调试,可以看到该函数定义如下:

我们继续跟进 make_cmd 函数。如下图,调用了 PT_query_expression::contextualize 进行上下文初始化,这个我们之前分析的 AST 树是一致的。根节点的 m_qe 成员指向 PT_query_expression。

我们继续用 s 跟进,看看 PT_query_expression::contextualize 函数做了什么,如下所示:首先对 with 语句做上下文初始化,因为此处 with 语句为空,所以该函数什么也不做。接下来执行了 Parse_tree_node::contextualize 将节点标记为上下文化。

接下来还执行了 m_body->contextualize(pc) 。我们前面分析过此处的 m_body 指的是 PT_query_specification。所以该函数为 PT_query_specification::contextualize。我们继续 s 跟进去查看

分别对PT_select_item_list 和 from_clauser做上下文初始化。  该函数还对 into 等字句做上下文初始化,因为我们的例子不涉及该函数,此处我们不做分析。下文我们不再跟进一一分析,最终上下文初始化后的 AST 树如下:

 

参考:https://www.freesion.com/article/64711249190/

标签:语句,engine,const,cmd,语法,源码,bool,MySQL,secondary
From: https://www.cnblogs.com/jkin/p/16899460.html

相关文章

  • 解决Mysql 5.7 不能插入中文的问题
    问题的解决方案问题描述:在学习DML插入中文数据时,发现出现了以下问题insertintotea(id,name)values(2,'徐凤年');ERROR1366(HY000):Incorrectstringv......
  • 【操作系统】Linux下安装mysql、jmeter
    mysql安装安装调试启动官网:https://dev.mysql.com/downloads/repo/yum/下载好需要的rpm源,使用Xftp将rpm传入虚拟机的/root目录下Xshell操作linux,或者直接虚拟机终端......
  • 微前端之四 • Single SPA 的源码实现
    深入了解一个库最好的办法是直接去看源代码,学习作者的设计模式、运行原理、代码风格等。并且动手跑起来,碰到不懂的地方打断点或者打印关键信息,一步一步去琢磨。很多流行库......
  • Mest SQL(1)---利用Python将Excel数据表导入MySQL数据库
    【最终呈现效果】【原始Excel数据表】【代码实现及注释】importpandasaspdimporttimeimportreti=time.strftime('%Y_%m_%d_%H%M%S',time.localtime())myexcel=r'G:\M......
  • JavaScript语法_流程控制语句和JavaScript语法_流程控制语句
    JavaScript语法_流程控制语句://1.语句以;结尾,如果一行只有一条语句则;可以省略(不建议)//1.语句以;结尾,如果一行只有一条语句则可以省略(不建议)leta=3......
  • Mysql历史介绍及多版本安装
    目录一、数据库概述1.1什么是数据库1.2数据库的分类1.3Mysql企业版本选择二、Windows多版本部署Mysql数据库2.1mysql5.6版本安装2.2mysql5.7版本安装2.3mysql8.0版......
  • MYSQL
    创建数据表 CREATETABLEIFNOTEXISTSstudent(idINT(4)NOTNULLAUTO_INCREMENTCOMMENT'学号',NAMEVARCHAR(30)NOTNULLDEFAULT'小明'COMMENT'姓名',PRI......
  • 提升mysql服务器性能(存储引擎与配置参数设置)
    服务层实现了与引擎无关的性能 frm用于记录结构 使用表锁 使用共享所和读锁支持全文索引 前缀索引  如果不对表进行操作尽可以进行压缩; myisamchk 命令行工具需......
  • Mysql命令行使用source执行.sql文件报错
    问题描述在windows上,使用命令行登录mysql,使用source命令执行xxx.sql文件,报错。将文件里的内容粘贴在命令行可以正确执行。原因连接mysql时没有设置编码解决mysql-u......
  • 2022-11-17 mysql列存储引擎-聚合中间缓存结果-分析
    摘要:mysql列存储引擎-聚合中间缓存结果-分析DML:TPCH表使用Q16selectp_brand,p_type,p_size,count(distinctps_suppkey)assupplier_cntfrompartsupp,partwh......