导语
SHOW CREATE TABLE
语句用于为指定表/视图显示创建的语句,本文将简要描述如何在MySQL源码里跟踪和学习此类语句的执行流程。(注:使用版本为Percona-Server-8.0.25-15
)
步骤
准备工作
编译调试版本的 mysql server
程序,创建数据库实例后建立一张示例表:
create table t1(c1 int);
有了示例表, 在客户端执行如下语句,就可以在服务端开始我们的语句跟踪了:
show create table t1;
断点设置
在如下函数/方法中设置断点(gdb):
dispatch_sql_command # 对sql语句做词法/语法解析,得到实际要运行的sql命令mysql_execute_command # 根据lex->sql_command值调用对应方法执行查询操作Sql_cmd_show_noplan::executeSql_cmd_show_create_table::execute_inner # '执行'show create table指令mysqld_show_create # 由Sql_cmd_show_create_table::execute_inner调用,获取表创建信息store_create_info # 根据表属性拼接建表字串
代码跟踪与阅读
通过断点查看上下文代码,通过打印变量信息等手段,可大致了解show create table t1
的执行流程,以下列出几个执行中较关键的位置,并对源码内容做注解说明:
- 断点位置1:Sql_cmd_show_create_table::execute_inner(THD *) sql_show.cc:408
代码上下文:
bool Sql_cmd_show_create_table::execute_inner(THD *thd) { // ... ... 注:为显示和说明方便,部分代码已被省略,可自行参阅源码读取更全面信息 // 将指定表加入至session的table list,并初始化表的锁信息;相当于让session知道,本次查询 // 将会用到这张表。 if (lex->query_block->add_table_to_list(thd, m_table_ident, nullptr, 0) == nullptr) return true; TABLE_LIST *tbl = lex->query_tables; // ... ... if (mysqld_show_create(thd, tbl)) return true; //断点位置 return false;}
- 断点位置2:mysqld_show_create(THD *, TABLE_LIST *) sql_show.cc:1206
代码上下文:
bool mysqld_show_create(THD *thd, TABLE_LIST *table_list) { // ... ... // 打开指定表/视图,获取显示数据信息所需的元数据锁(MDL) bool open_error = open_tables(thd, &table_list, &counter, MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL); // 'show create table'语句也可以显示view的创建信息,本方法根据对应table element // 的属性来确定调用view_store_create_info()还是store_create_info(). if (table_list->is_view()) view_store_create_info(thd, table_list, &buffer); else if (store_create_info(thd, table_list, &buffer, nullptr, false)) //断点位置 goto exit; // 获取到表/视图创建信息后,本方法还会组一张虚拟表返回给客户端,客户端根据得到的表头和 // 数据内容,完成信息的展示,其结果示例如: // +-------+----------------------------+ // | Table | Create Table | // +-------+----------------------------+ // | t1 | CREATE TABLE `t1` `c1` ... | // +-------+----------------------------+ if (table_list->is_view()) { field_list.push_back(new Item_empty_string("View", NAME_CHAR_LEN)); field_list.push_back(new Item_empty_string( "Create View", max(buffer.length(), 1024U))); field_list.push_back( new Item_empty_string("character_set_client", MY_CS_NAME_SIZE)); field_list.push_back( new Item_empty_string("collation_connection", MY_CS_NAME_SIZE)); } else { field_list.push_back(new Item_empty_string("Table", NAME_CHAR_LEN)); // 1024 is for not to confuse old clients field_list.push_back(new Item_empty_string( "Create Table", max(buffer.length(), 1024U))); } // ... ... }
- 断点位置3: store_create_info(THD *, ...) sql_show.cc:1885
代码上下文:
bool store_create_info(THD *thd, TABLE_LIST *table_list, String *packet, HA_CREATE_INFO *create_info_arg, bool show_database) { // ... ... // 根据指定表的属性,确定创建语句 if (share->tmp_table) packet->append(STRING_WITH_LEN("CREATE TEMPORARY TABLE ")); else packet->append(STRING_WITH_LEN("CREATE TABLE ")); if (create_info_arg && (create_info_arg->options & HA_LEX_CREATE_IF_NOT_EXISTS)) packet->append(STRING_WITH_LEN("IF NOT EXISTS ")); if (table_list->schema_table) alias = table_list->schema_table->table_name; else { if (lower_case_table_names == 2) alias = table->alias; else { alias = share->table_name.str; } } // ... ... // 表的列信息生成 for (ptr = table->field; (field = *ptr); ptr++) { // ... ... if (ptr != table->field) packet->append(STRING_WITH_LEN(",\n")); packet->append(STRING_WITH_LEN(" ")); append_identifier(thd, packet, field->field_name, strlen(field->field_name)); packet->append(' '); // check for surprises from the previous call to Field::sql_type() if (type.ptr() != tmp) type.set(tmp, sizeof(tmp), system_charset_info); else type.set_charset(system_charset_info); // ... ...}
store_create_info()
是一个比较'大支'的代码(600+行), 其原理本质上就是遍历和指定表相关的所有属性,并逐个将对应的创建信息塞入到事先预分配的String buffer
里。
因为表的属性信息是在调用store_create_info
前实时获取的,所以如果在我们创建原始表后对表属性和结构做了变更,在show create table
时,是能看到信息的变化的,以本文示例t1为例,我们对其做如下操作:
create index idx_c1 on t1(idx);alter table t1 add column c2 int;
通过show create table t1
,是能够看到前后显示信息的不同的。
原始表输出信息:
> show create table t1;+-------+----------------------------------------------------------+| Table | Create Table |+-------+----------------------------------------------------------+| t1 | CREATE TABLE `t1` ( |`c1` int DEFAULT NULL | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci |+-------+----------------------------------------------------------+1 row in set (1 hour 25 min 45.87 sec)
修改表后输出信息
> show create table t1;+-------+----------------------------------------------------------+| Table | Create Table |+-------+----------------------------------------------------------+| t1 | CREATE TABLE `t1` ( |`c1` int DEFAULT NULL, | `c2` int DEFAULT NULL, |KEY `idx_c1` (`c1`) |) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci |+-------+----------------------------------------------------------+1 row in set (0.00 sec)
结语
本文简要介绍了如何通过源码对MySQL show create table
语句的执行流程进行学习和跟踪,
对其他 show
类型的语句如show create database
,show create view
,show create index
等,具备不错的参考作用,有兴趣的朋友亦可做尝试和探索 🙂
Enjoy GreatSQL 🙂