源码分析系列的第 2 篇文章,我们来聊聊 select *
中的星号是怎么展开为表中所有字段的。
本文内容基于 MySQL 8.0.32 源码。
正文
1. 整体介绍
对于 select * from table
中的星号,我们再熟悉不过了:它告诉 MySQL 返回表所有字段的内容。
MySQL 服务端收到 select 语句之后,会在 server 层把星号展开为表中的所有字段,然后告诉存储引擎返回这些字段的内容。
对于存储引擎来说,它只需要按照 server 层的要求返回指定字段的内容即可,它不知道(也不需要知道)客户端是要求返回表中所有字段,还是部分字段的内容。
select *
中的星号展开为表中所有字段涉及 2 个阶段:
-
词法 & 语法分析阶段:标记 select 字段列表中包含几个星号。
-
查询准备阶段:把星号展开为表中所有字段。
2. 源码分析
2.1 Item_asterisk::itemize()
// sql/item.cc
bool Item_asterisk::itemize(Parse_context *pc, Item **res) {
...
pc->select->with_wild++;
return false;
}
多表连接时,select 字段列表中可能会包含多个星号,词法 & 语法分析阶段,每碰到 select 字段列表中的一个星号,Item_asterisk::itemize()
就会给 pc->select->with_wild
属性加 1。
pc->select
是 Query_block
对象的指针,定义如下:
// sql/parse_tree_node_base.h
struct Parse_context {
...
Query_block *select; ///< Current Query_block object
...
};
后面 Query_block::prepare()
访问的 with_wild
属性就是这里的 pc->select->with_wild
。
2.2 Query_block::prepare()
// sql/sql_resolver.cc
bool Query_block::prepare(THD *thd, mem_root_deque<Item *> *insert_field_list) {
...
if (with_wild && setup_wild(thd)) return true;
...
}
prepare() 方法中,关于 select *
的逻辑比较简单,就这一行。
如果 with_wild 大于 0,则调用 setup_wild(thd)
,处理 select 字段列表中星号展开为表中所有字段的逻辑。
2.3 Query_block::setup_wild()
/