MySQL frm 文件详解

本文基于 MySQL 5.7.35 逐字节解读 frm 文件的内容,本来想通过抽象的方式,来说清楚 frm 文件中每个字节的含义,写了一半发现根本行不通,因为除了 frm header block 区域的 Offset 是确定的,其它区域(如索引、字段、注释等)长度不确定,Offset 也是不确定的,很难描述清楚,因此,本文使用一个具体的表来说明 frm 文件中每个字节的含义,表结构如下:

 1CREATE TABLE `t_create_bit_enum_set` (
 2  `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增主键',
 3  `i1` INT(10) UNSIGNED NOT NULL DEFAULT '0' COMMENT '整数1',
 4  `city` ENUM('北京','上海','广州','深圳','天津','重庆') DEFAULT NULL COMMENT '城市',
 5  `color` SET('红','橙','黄','绿','青','蓝','紫') DEFAULT '蓝' COMMENT '颜色',
 6  `str1` VARCHAR(255) DEFAULT NULL COMMENT '字符串1',
 7  `str2` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '字符串3',
 8  `str3` CHAR(20) DEFAULT NULL COMMENT '字符串4',
 9  `status` BIT(1) NOT NULL DEFAULT b'1' COMMENT '状态',
10  PRIMARY KEY (`id`) COMMENT '主键索引',
11  KEY `idx_str2_i1` (`str2`, `i1`) KEY_BLOCK_SIZE=1024 COMMENT 'str2_i1索引'
12) ENGINE=INNODB DEFAULT CHARSET=utf8 MIN_ROWS=1 MAX_ROWS=100000 COMMENT='带enum/set/bit字段的表';

说明:各小节标题直接按照源码中的意思,使用了英文作标题(本来开始是用中文的,不能很好的把代码中表示的意思翻译出来)

1. 文件头

  • frame header block,范围为一个 IO 块,大小为 IO_SIZE = 4096 字节,Offset 0000 ~ 4095,分为 2 个区域:
    • frm header section占用 64 字节,Offset 0000 ~ 00063,主要存储 frm 文件版本信息、MySQL 版本信息、frm 各区域的开始 Offset、frm 各区域的长度等信息,具体见 frm header section 表格
    • formnames section占用 7 字节,Offset 0064 ~ 0070,其中 Offset 0064 ~ 0066 存储的内容已废弃,Offset 0067 ~ 0070 存储的内容和 Offset 0010 ~ 0013 是完全一样的,在 MySQL 8.0 中,formnames section 已经被废弃

1.1 frm header section

Offset 存储值 字节数 说明
00 254 1 固定值
01 1 1 固定值
02 10 1 frm 文件版本号,表中不包含 varchar 字段时,值为 9
表中包含 varchar 字段时值为 10
03 12 1 存储引擎类型,12 表示 InnoDB
04 3 1 没实际意义,见表格后面的说明
05 0 1 没有写入值,默认 0
06 ~ 07 4096 2 hybrid section 开始处的 Offset
08 ~ 09 1 2 没实际意义,见表格后面的说明
10 ~ 13 12288 4 fields block 开始处的 Offset,按 4096 字节对齐
12288 = 8192 + 4096
14 ~ 15 735 2 index section 长度,如果 2 字节存储不下索引的长度,则 Offset 14 ~ 15 的所有位全部置为 1,即 0xffff,然后把 index section 长度存储到 Offset 47 ~ 50 字节处
如果 2 字节能存储下索引的长度,则 Offset 14 ~ 15 处存储的值和 Offset 47 ~ 50 处存储的值相等
16 ~ 17 1032 2 record section 长度,同 Offset 8458 ~ 8459
18 ~ 21 100000 4 计划在表中 最多 会存储多少条记录,在建表时通过 MAX_ROWS 选项设置,NDB 存储引擎使用,在 NDB Cluster 7.5.4 中已经废弃
22 ~ 25 1 4 计划在表中最少会存储多少条记录,在建表时通过 MIN_ROWS 选项设置,MEMORY 引擎会使用这个字段,作为内存使用量的提示
26 0 1 表只有 1 行数据且没有索引时,此处值为 1(系统表?),否则为 0
27 2 1 固定值,表示一个字段在 frm 中需要 17 字节存储
28 ~ 29 100 2 index section 实际内容占用字节数(index section 长度大于 实际内容占用字节数),Offset 4096 ~ 4195 区间的长度
30 ~ 31 9 2 创建表时的选项
32 0 1 固定值,表示是大于 MySQL 3.23 版本
33 5 1 固定值,表示是 MySQL 5.x 创建的 frm 文件
MySQL 4.x 版本使用 varchar 来实现 char
MySQL 5.0 开始 char 有自己的实现方式
34 ~ 37 0 4 行平均长度,建表时通过 AVG_ROW_LENGTH 设置,MyISAM 引擎使用,和 MAX_ROWS * AVG_ROW_LENGTH 计算得到表最大数据量
38 33 1 字符集 ID
39 0 1 固定值,代码注释:In future versions, we will store in fileinfo[39] the values of the TRANSACTIONAL and PAGE_CHECKSUM clauses of CREATE TABLE
40 0 1 行类型,-1:未定义、0:默认、1:FIXED、2:DYNAMIC、3:COMPRESSED、4:REDUNDANT、5:COMPACT
41 0 1 Offset 38 字符集右移了 8 位(csid >> 8),不知道干嘛用?
42 ~ 43 0 2 表统计信息采样页数,建表时通过 STATS_AUTO_RECLAC 设置,表示是否自动重新计算存储在磁盘中的表统计信息
44 0 1 是否自动重新计算采样信息,0:默认、1:自动重新计算、2:不自动重新计算,建表时通过 STATS_SAMPLE_PAGES,表示计算表统计信息时采样的页数
45 0 1 固定值,似乎没实际意义
46 0 1 固定值,似乎没实际意义
47 ~ 50 735 4 index section 实际内容占用字节数,详细说明见 Offset 14 ~ 15
51 ~ 54 50735 4 MySQL 版本号
55 ~ 58 37 4 extra section 占用字节数
59 ~ 60 0 2 extra_rec_buf_length,这个只有 partition 存储引擎会用到,其它存储引擎此处值为 0
61 null 1 默认存储引擎,只有 partition 存储引擎用到了
62 ~ 63 0 2 索引块大小,建表或者索引时通过 KEY_BLOCK_SIZE 选项设置

注意index section的长度共有 3 处存储,Offset 14 ~ 15、Offset 47 ~ 50 存储index section 长度;Offset 28 ~ 29 存储index section 实际内容占用字节数

MAX_ROWSMIN_ROWSAVG_ROW_LENGTHSTATS_AUTO_RECLACSTATS_SAMPLE_PAGESKEY_BLOCK_SIZE 这几个在建表或建索引时指定的选项的详细说明,见官方文档:CREATE TABLE

以下关于 Offset 04、Offset 08 ~ 09、Offset 10 ~ 13 处的说明都和 make_new_entry() 方法有关,为了内容完整性所以写出来了,对其中细节不兴趣的可以不看

Offset 04sql/table.cc:create_frm() 中写入值 1,在 sql/table.cc:make_new_entry() 中会修改,是有个计算逻辑算出来的,按照 5.7.35 版本中的逻辑,算出来的值固定是 3,即 Offset 04 的内容先被设置为 1,后被修改为 3

Offset 08 ~ 09sql/table.cc:make_new_entry() 写入值,按照 5.7.35 版本中的逻辑,算出来的值是 1

Offset 04Offset 08 ~ 09 的值最终都是通过 sql/table.cc:make_new_entry() 写入的; 在 MariaDB 10.0.2MySQL 8.0 都已经把 make_new_entry() 删除了。并且在 MariaDB 10.0.2 中找到了版本修改说明,明确说明了 make_new_entry() 是死代码,这个方法的作用是读出 字段块 的开始地址的,直接改为 Offset 10 ~ 13 处读取了,具体说明见 MariaDB 10.0.2 更新说明

Offset 10 ~ 13 处的值,在 sql/table.cc:make_new_entry() 3678 ~ 3709 行 有修改逻辑,满足 if 条件时,会给 Offset 10 ~ 13 的值加上 4096(IO_SIZE),但是因为 if (64+length+n_length+(names+1)*4 > maxlength) 中的 names 的值是 0,这个 if 判断永远不会成立,所以实际 make_new_entry() 永远不会修改 Offset 10 ~ 13 的值

Offset 320 表示是大于 MySQL 3.23 的版本,1 表示是小于等于 MySQL 3.23 版本,在大于 MySQL 的版本中,会从 Offset 34 处读取行平均长度、从 Offset 38、41 读取字符集、从 Offset 40 读取行类型、从 Offset 42 读取表统计信息采样页数、从 Offset 44 读取是否自动计算采样信息的标识

1.2 formnames section

Offset 存储值 字节数 说明
64 ~ 65 // 2 form 区域的名字,在 5.7.35 版本中,form 区域名字为空字符串,所以此处存储内容为(//)
66 null 1 form section 名字 null 结束符
67 ~ 70 8192 4 Offset 10 ~ 13 存储的内容一样

Offset 64 ~ 66 虽然还写入了值,但是实际上已废弃

Offset 67 ~ 70 在打开表时 get_form_pos() 中还用到了,但实际从 Offset 10 ~ 13 处读取就可以了,Offset 60 ~ 70 已经没有存在的必要了

1.3 blank section

Offset 存储值 字节数 说明
71 ~ 4095 null 4025 空白区域,全部字节都为 null

2 hybrid block

2.1 index section

Offset 存储值 字节数 说明
4096 2 1 索引数量,Offset 4096 ~ 4099 的详细说明见表格后面的代码
4097 3 1 索引数量或者字段数量
4098 ~ 4099 null 2 字段数量或者 null
4100 ~ 4101 51 2 索引名长度 + 索引注释长度,Offset 4145 ~ 4195
4102 ~ 4103 4096 2 主键索引开始
索引标识,key->flags ^ HA_NOSAME,HA_NOSAME = 1
4104 ~ 4105 4 2 索引长度(key_length),即索引中多个字段长度之和,不包含存储可变字段长度占用的字节数
4106 1 1 索引中用户自定义字段数量(user_defined_key_parts)
4107 0 1 索引数据结构(algorithm),0:UNDEF、1:BTREE、2:RTREE、3:HASH、4:FULLTEXT
4108 ~ 4109 0 2 索引块大小(block_size),建表时 KEY_BLOCK_SIZE 指定,见 frm header block 处说明
4110 ~ 4111 32769 2 字段是表中第几个字段,key_part->fieldnr(0)+1+FIELD_NAME_USED(32768)
索引 id 字段开始
4112 ~ 4113 2 2 索引 id 字段在 server 层格式的行中的 Offset,key_part->offset(0)+data_offset(1)+1,keypart->offset:字段在 Offset,包含存储变长字段的长度占用的字节,data_offset:表中空字段标记位占用字节数
4114 0 1 id 字段排序,写死的值 0
4115 ~ 4116 26 2 索引类型(key_type)???
4117 ~ 4118 4 2 字段存储占用字节数(length)
索引 id 字段结束
主键索引结束
4119 ~ 4120 36865 2 索引标识
idx_str2_i1 索引开始
4121 ~ 4122 196 2 索引长度,不包含存储可变字段长度占用的字节数
4123 2 1 索引中用户自定义字段数量
4124 0 1 索引数据结构,枚举值 见 Offset 4507
4125 ~ 4126 1024 2 索引块大小,为什么这里块大小是 1024,和主键索引不一样???
4127 ~ 4128 32774 2 字段是表中第几个字段,key_part->fieldnr(5)+1+FIELD_NAME_USED(32768)
索引 str2 字段开始
4129 ~ 4130 779 2 str2 字段在 server 层格式的行中的 Offset,key_part->offset(777)+data_offset(1)+1
4131 0 1 str2 字段排序,写死的值 0
4132 ~ 4133 0 2 字段类型(key_type)?
4134 ~ 4135 192 2 字段存储占用字节数(length),变长字段也不包含存储长度占用的字节数
str2 字段结束
4136 ~ 4137 32770 2 字段是表中第几个字段,key_part->fieldnr(1)+1+FIELD_NAME_USED(32768)
索引 i1 字段开始
4138 ~ 4139 6 2 i1 字段在 server 层格式的行中的 Offset,key_part->offset(4)+data_offset(1)+1
4140 0 1 i1 字段排序,,写死的值 0
4141 ~ 4142 26 2 字段类型(key_type)?
4143 ~ 4144 4 2 字段存储占用字节数(length)
索引 i1 字段结束
idx_str2_i1 索引结束
4145 255 1 索引名字分隔符
索引名字区域开始
4146 ~ 4152 PRIMARY 7 主键索引名字
4153 255 1 索引名字分隔符
4154 ~ 4164 idx_str2_i1 11 idx_str2_i1 索引名
4165 255 1 索引名字分隔符
索引名字区域结束
4166 null 1 索引名字和索引之间的分隔符
4167 ~ 4168 12 2 主键索引 comment 的长度
索引注释区域开始
4169 ~ 4180 主键索引 12 主键索引 comment
4181 ~ 4182 13 2 idx_str2_i1 索引 comment 的长度
4183 ~ 4195 str2_i1索引 13 str2_i1索引 索引 comment
索引注释区域结束
4196 ~ 4830 null 635 这段区域为空白区域,每个字节的值都是 null

Offset 4096 ~ 4099 存储的内容取决于表中索引数量、或者表中所有索引的字段数量是否大于 127,具体见如下代码:

 1// sql/unireg.cc
 2static uint pack_keys(uchar *keybuff, uint key_count, KEY *keyinfo,
 3                      ulong data_offset)
 4  // ...... 此处省略 N 行
 5
 6  // 如果表中索引数量或者索引字段数量大于 127 个
 7  if (key_count > 127 || key_parts > 127)
 8  {
 9    // keybuff[0] = Offset 4096
10    // 存储的值为:索引数量 + 128,其中 128 是个标志位,在读取 frm 文件的时候会用到
11    keybuff[0]= (key_count & 0x7f) | 0x80;
12
13    // keybuf[1] = Offset 4097
14    // 存储的值为:索引数量 - 128
15    keybuff[1]= key_count >> 7;
16
17    // keybuf[2 ~ 3] = Offset 4098 ~ 4099
18    // 存储的值为:索引字段数量
19    int2store(keybuff+2,key_parts);
20  }
21  else // 表中索引字段数量小于等于 127
22  {
23    // keybuf[0] = Offset 4096,存储的值为:索引数量
24    keybuff[0]=(uchar) key_count;
25    
26    // keybuf[1] = Offset 4097,存储的值为:索引中字段数量
27    keybuff[1]=(uchar) key_parts;
28    
29    // keybuf[2 ~ 3] = Offset[4098 ~ 4099],存储的值为 null
30    keybuff[2]= keybuff[3]= 0;
31  }
32
33  // ...... 此处省略 N 行
34}

2.2 record section(with fields default value)

record section: 在代码中也叫做空行(empty rec),是存储每个字段默认值的区域

Offset 存储值 字节数 说明
4831 253 1 行中允许为 null 的字段对应标记位
4832 ~ 4835 null 4 id 字段的默认值(没有默认值,所以为 null
4836 ~ 4839 0 4 i1 字段默认值
4840 null 1 city 字段默认值
4841 32 1 color 字段默认值,set 中的 转换为 32 了
4842 ~ 4843 null 2 str1 字段字段默认值的长度,默认值为 null,长度也存为 null
4844 ~ 5608 null 765 str1 字段默认值,255 * 3(utf8 每个字符最多占用 3 字节) = 765
5609 0 1 str2 字段默认值的长度,默认值为空字符串,长度为 0
5610 ~ 5801 "" 192 str2 默认值,空字符串,实际 5610 ~ 5801 每个字节存储的都是 null
5802 ~ 5861 ' ' 60 str3 默认值(注意:char 类型字段不需要单独字节来存储长度
字段字符集为二进制,则每个字节都存储 null
字段字符集为非二进制,则每个字节存储 ' '(空格)
5862 1 1 status 字段默认值,当 1 个字节的 char 字符处理

Offset 4831 说明:253 = 11111101,接下来从右往左解释各 bit:
第 1 位:1 表示 city 字段的默认值为 null
第 2 位:0 表示 color 字段的默认值不为 null
第 3 位:1 表示 str1 字段的默认值为 null
第 4 位:1 表示 str3 字段的默认值为 null
第 5 ~ 8 位:全都是因为没有被使用而被置为了 1,见如下代码:

 1// sql/unireg.cc
 2static bool make_empty_rec(THD *thd, File file,
 3			   uint table_options,
 4			   List<Create_field> &create_fields,
 5			   uint reclength,
 6                           ulong data_offset,
 7                           handler *handler)
 8{
 9    // ...... 此处省略 N 行代码
10
11  /*
12    We need to set the unused bits to 1. If the number of bits is a multiple
13    of 8 there are no unused bits.
14  */
15  if (null_count & 7)
16    *(null_pos + null_count / 8)|= ~(((uchar) 1 << (null_count & 7)) - 1);
17
18    // ...... 此处省略 N 行代码
19}

2.3 extra section

Offset 存储值 字节数 说明
5863 ~ 5684 0 2 连接字符串(connect_string)的长度,federated 存储引擎使用
N/A N/A N/A connect_string
5865 ~ 5866 6 2 存储引擎类型的长度
5867 ~ 5872 InnoDB 6 存储引擎类型
5873 ~ 5878 null 6 存储引擎分区信息占位区域,partition 存储引擎使用
N/A N/A N/A 一个或多个 FULLTEXT 索引的解析器名字
N/A N/A N/A 表注释超过最大长度(180 字节),把整个表注释存储在这个区域
5879 ~ 5880 17 2 format section 长度
format section 区域开始
5881 ~ 5884 0 4 表的存储介质,0:默认 1:磁盘(HA_SM_DISK)2:内存(HA_SM_MEMORY)
5885 ~ 5886 0 2 这 2 个字节现在未使用,代码中写死了值为 0
N/A N/A N/A table space 名字
5887 null 1 table space 名字的 null 结束符
5888 0 1 id 字段的存储介质、存储格式
5889 0 1 i1 字段的存储介质、存储格式
5890 0 1 city 字段的存储介质、存储格式
5890 0 1 color 字段的存储介质、存储格式
5991 0 1 str1 字段的存储介质,存储格式
5992 0 1 str2 字段的存储介质,存储格式
5993 0 1 str3 字段的存储介质,存储格式
5995 0 1 status 字段的存储介质,存储格式
format section 区域结束
5896 ~ 5897 0 2 InnoDB 压缩页压缩算法名长度
N/A N/A N/A InnoDB 压缩页压缩算法名字
5898 ~ 5899 0 2 InnoDB 页加密算法名长度
N/A N/A N/A InnoDB 压缩页算法名字
5900 ~ 8191 null 2292 空白区域

Offset 5873 ~ 5878,如果是 partition 存储引擎,会用 4 字节存储分区信息的长度,然后接下来的一段区域存储分区信息以及 null 结束符,然后 1 字节存储是否自动分区的标记,对于我们创建的这个 InnoDB 测试表来说,是 6 个字节的空白区域

上面表格中,如果行中存在 N/A,说明该行在本次测试表的 frm 文件中是不存在的,把这些行还放在表格中,是为了表示如果相应的区域存在会在这个位置

3. fields block

3.1 form section

Offset 存储值 字节数 说明
8192 ~ 8193 717 2 ???
8194 ~ 8195 4096 2 Offset 8192 ~ 8193 + 1000 按 4096 字节对齐
???
8238 27 1 表注释长度
8239 ~ 8418 见说明
->
180 带enum/set/bit字段的表\0...\0
表注释区域总共长度是 180 字节
实际注释不到 180 字节,后面部分用 null 填充
8419 ~ 8447 null 29 空白区域
8448 1 1 screen 数量,表示 1 屏能显示下所有字段
8449 ??? 1 ???
8450 ~ 8451 11 2 表中字段数量
8452 ~ 8453 111 2 screen section 长度,同 Offset 8480 ~ 8481
8454 ~ 8455 1083 2 所有字段 field->length 之和
8456 ~ 8457 0 2 field->unireg_check = Field::NO_EMPTYfield->unireg_check & MTYP_NOEMPTY_BIT 字段数
8458 ~ 8459 1032 2 field->pack_length 之和 + date_offset(=1)
8460 ~ 8461 41 2 field->field_name 长度之和 + 字段数量 * 1
8462 ~ 8463 2 2 enum、set 选项列表数量(相同选项列表去重)
8464 ~ 8465 15 2 enum、set 选项列表中的选项数量
8466 ~ 8467 74 2 enum、set 选项列表区域长度
选项长度之和 + 选项数量 * 1 + 选项列表数量 * 2
8468 ~ 8469 0 2 time_stamp_pos,???
8470 ~ 8471 80 2 screen 列数
8472 ~ 8473 22 2 screen 行数
8474 ~ 8475 4 2 允许为 null 的字段数量
8476 ~ 8477 67 2 字段注释长度,field->comment.length 之和
8478 ~ 8479 0 2 虚拟列信息长度???

3.2 screen section

Offset 存储值 字节数 说明
8480 ~ 8481 111 2 screen 内容长度 Offset 8480 ~ 8589
8482 9 1 一屏上实际显示的行数?
8483 8 1 一屏上显示的字段数?
8484 2 1 ???
8485 20 1 ???
8486 41 1 ???
8487 ~ 8526 空格 40 40 个空格(" ")
8527 null 1 screen 头部区域结束???
8528 4 1 id 字段显示在第 4 行
id 字段开始
8529 0 1 固定值 0
8530 3 1 字段名长度 + 1
8531 ~ 8533 id\0 3 字段名 + null 结束符
id 字段结束
8534 5 1 i1 字段显示在第 5 行
i1 字段开始
8535 0 1 固定值 0
8536 3 1 字段名长度 + 1
8537 ~ 8539 i1\0 3 字段名 + null 结束符
i1 字段结束
8540 6 1 city 字段显示在第 6 行
city 字段开始
8541 0 1 固定值 0
8542 5 1 字段名长度 + 1
8543 ~ 8547 city\0 5 字段名 + null 结束符
city 字段结束
8548 7 1 color 字段显示在第 7 行
color 字段开始
8549 0 1 固定值 0
8550 6 1 字段名长度 + 1
8551 ~ 8556 color\0 6 字段名 + null 结束符
color 字段结束
8557 8 1 str1 字段显示在第 8 行
str1 字段开始
8558 0 1 固定值 0
8559 5 1 字段名长度 + 1
8560 ~ 8564 str1\0 5 字段名 + null 结束符
str1 字段结束
8565 9 1 str2 字段显示在第 9 行
8566 0 1 固定值 0
8567 5 1 字段名长度 + 1
8568 ~ 8572 str2\0 5 字段名 + null 结束符
str2 字段结束
8573 10 1 字段 str3 显示在第 10 行
str3 字段开始
8574 0 1 固定值 0
8575 5 1 字段名长度 + 1
8576 ~ 8580 5 str3\0 字段名 + null 结束符
str3 字段结束
8581 11 1 字段 status 显示在第 11 行
status 字段开始
8582 0 1 固定值 0
8583 7 1 字段名长度 + 1
8584 ~ 8590 status\0 7 字段名 + null 结束符
status 字段结束

3.3 fields section

Offset 存储值 字节数 说明
8591 4 1 field->row,字段名显示在屏幕上第几行
字段 id 开始
8592 3 1 field->col,字段名显示在屏幕上占用宽度
含 null 结束符,以下其它字段同
8593 10 1 field->sc_length,字段值显示在屏幕上占用宽度
8594 ~ 8595 10 2 field->length,字段存储最大值时,显示在屏幕上占用宽度
8596 ~ 8598 2 3 field->offset(=0)+1+data_offset(=1),字段在行中的 Offset
8599 ~ 8600 26 2 field->pack_flag,字段标识
8601 15 1 field->unireg_check, ???
虚拟列,此值会加上 Field::GENERATED_FIELD(128)
8602 0 1 geometry 字段此值在代码中写死了为 0
字段没有字符集,此值代码中写死了为 0
有字符集,field->charset->number(=33) >> 8
8603 0 1 field->interval_id,enum、set 选项列表 ID
8604 3 1 field->sql_type(=MYSQL_TYPE_LONG),字段类型
8605 33 1 geometry 字段:field->geom_type,字段类型
字段没有字符集,此值代码中写死了为 0
有字符集:field->charset->number,字符集 ID
8606 ~ 8607 12 2 field->comment.length,字段注释长度
字段 id 结束
8608 5 1 field->row
字段 i1 开始
8609 3 1 field->col
8610 10 1 field->sc_length
8611 ~ 8612 10 2 field->length
8613 ~ 8615 6 3 field->offset(=4)+1+data_offset(=1)
8616 ~ 8617 26 2 field->pack_flag
8618 0 1 field->unireg_check
8619 0 1 field->charset->number(=33) >> 8
8620 0 1 field->interval_id
8621 3 1 field->sql_type(=MYSQL_TYPE_LONG)
8622 0 1 field->charset->number(=33)
8623 ~ 8624 7 2 field->comment.length
字段 id 结束
8625 6 1 field->row
字段 city 开始
8626 5 1 field->col
8627 6 1 field->sc_length
8628 ~ 8629 6 2 field->length
8630 ~ 8632 10 3 field->offset(=8) + 1 + data_offset(=1)
8633 ~ 8634 33032 2 field->pack_flag
8635 16 1 field->unireg_check(=INTERVAL_FIELD)
8636 0 1 field->charset->number(=33) >> 8
8637 1 1 field->interval_id
8638 247 1 field->sql_type(=MYSQL_TYPE_ENUM)
8639 33 1 field->charset->number(=33)
8640 ~ 8641 6 1 field->comment.length
字段 city 结束
8642 7 1 field->row
字段 color 开始
8643 6 1 field->col
8644 ~ 8645 39 1 field->sc_length
8646 ~ 8647 39 2 field->length
8648 ~ 8650 11 3 field->offset(=9)+1+data_offset(=1)
8651 ~ 8652 33288 2 field->pack_flag
8653 17 1 field->unireg_check(BIT_FIELD)
8654 0 1 field->charset->number(=33) >> 8
8655 248 1 field->sql_type(=MYSQL_TYPE_SET)
8656 33 1 field->charset->number(=33)
8657 ~ 8658 6 2 field->comment.length
字段 color 结束
8659 8 1 field->row
字段 str1 开始
8660 5 1 field->col
8661 74 1 field->sc_length
8662 ~ 8663 765 2 field->length
8664 ~ 8666 10 3 field->offset(=10)+1+data_offset(=1)
8667 ~ 8668 32768 2 feild->pack_flag
8669 0 1 field->unireg_check(=NONE)
8670 0 1 field->charset->number(33) >> 8
8671 0 1 field->interval_id
8672 15 1 field->sql_type(=MYSQL_TYPE_VARCHAR)
8673 33 1 field->charset->number(=33)
8674 ~ 8675 5 2 field->comment.length
字段 str1 结束
8676 9 1 field->row
字段 str2 开始
8677 5 1 field->col
8678 74 1 field->sc_length
8679 ~ 8680 192 2 field->length
8681 ~ 8683 779 3 field->offset(=777)+1+data_offset(=1)
8684 ~ 8685 0 2 field->pack_flag
8686 0 1 field->unireg_check(=NONE)
8687 0 1 field->charset->number(=33) >> 8
8688 0 1 field->interval_id
8689 15 1 field->sql_type(=MYSQL_TYPE_VARCHAR)
8690 33 1 field->charset->number(=33)
8691 ~ 8692 10 2 field->comment.length
字段 str2 结束
8693 10 1 field->row
字段 str3 开始
8694 5 1 field->col
8695 60 1 field->sc_length
8696 ~ 8697 60 2 field->length
8698 ~ 8700 972 3 field->offset(=970)+1+data_offset(=1)
8701 ~ 8702 32768 2 field->pack_flag
8703 0 1 field->unireg_check(=NONE)
8704 0 1 field->interval_id
8705 0 1 field->charset->number(=33) >> 8
8706 254 1 field->sql_type(=MYSQL_TYPE_STRING)
8707 33 1 field->charset->number
8708 ~ 8709 10 2 field->comment.length
字段 str3 结束
8710 11 1 field->row
字段 status 开始
8711 7 1 field->col
8712 1 1 field->sc_length
8713 ~ 8714 1 2 field->length
8715 ~ 8717 1032 3 field->offset(=1030)+1+data_offset(=1)
8718 ~ 8719 4098 2 field->pack_flag
8720 0 1 field->unireg_check(=NONE)
8721 0 1 field->interval_id
8722 0 1 field->charset->number(=33) >> 8
8723 16 1 field->sql_type(=MYSQL_TYPE_BIT)
8724 33 1 field->charset->number(=33)
8725 ~ 8726 6 2 field->comment.length
字段 status 结束
8727 255 1 字段名字分隔符
字段名开始
8728 ~ 8729 id 2 id 字段名
8730 255 1 分隔符
8731 ~ 8732 i1 2 i1 字段名
8733 255 1 分隔符
8734 ~ 8737 city 4 city 字段名
8738 255 1 分隔符
8739 ~ 8743 color 5 color 字段名
8744 255 1 分隔符
8745 ~ 8748 str1 4 str1 字段名
8749 255 1 分隔符
8750 ~ 8753 str2 4 str2 字段名
8754 255 1 分隔符
8755 ~ 8758 str3 4 str3 字段名
8759 255 1 分隔符
8760 ~ 8765 status 6 status 字段名
8766 255 1 分隔符
8767 null 1 null 结束符
字段名结束
8768 255 1 enum、set 选项分隔符
enum、set 选项开始
city 字段 enum 选项开始
8769 ~ 8774 北京 6 city - 北京
8775 255 1 分隔符
8776 ~ 8781 上海 6 city - 上海
8782 255 1 分隔符
8783 ~ 8788 广州 6 city - 广州
8789 255 1 分隔符
8790 ~ 8795 深圳 6 city - 深圳
8796 255 1 分隔符
8797 ~ 8802 天津 6 city - 天津
8803 255 1 分隔符
8804 ~ 8809 重庆 6 city - 重庆
8810 255 1 分隔符
8811 null 1 null 结束符
city 字段 enum 选项结束
8812 255 1 enum、set 选项分隔符
color 字段 set 选项开始
8813 ~ 8815 3 color - 红
8816 255 1 分隔符
8817 ~ 8719 3 color - 橙
8820 255 1 分隔符
8821 ~ 8823 3 color - 黄
8824 255 1 分隔符
8825 ~ 8827 绿 3 color - 绿
8828 255 1 分隔符
8829 ~ 8831 3 color - 青
8832 255 1 分隔符
8833 ~ 8735 3 color - 蓝
8836 255 1 分隔符
8837 ~ 8839 3 color - 紫
8840 255 1 分隔符
8841 null 1 null 结束符
color 字段 set 选项结束
enum、set 选项结束
8842 ~ 8853 自增主键 12 id 字段注释
字段注释开始
8854 ~ 8860 整数1 7 i1 字段注释
8861 ~ 8866 城市 6 city 字段注释
8867 ~ 8872 颜色 6 color 字段注释
8873 ~ 8882 字符串1 10 str1 字段注释
8883 ~ 8892 字符串3 10 str2 字段注释
8893 ~ 8902 字符串4 10 str3 字段注释
8903 ~ 8908 状态 6 status 字段注释
字段注释结束
N/A N/A N/A 虚拟列表达式区域

field->unireg_check 枚举值定义:

 1// sql/field.h:671
 2/* 
 3We use three additional unireg types for TIMESTAMP to overcome limitation 
 4of current binary format of .frm file. We'd like to be able to support 
 5NOW() as default and on update value for such fields but unable to hold 
 6this info anywhere except unireg_check field. This issue will be resolved
 7in more clean way with transition to new text based .frm format.
 8See also comment for Field_timestamp::Field_timestamp().
 9*/
10enum utype  { NONE,DATE,SHIELD,NOEMPTY,CASEUP,PNR,BGNR,PGNR,YES,NO,REL,
11	CHECK,EMPTY,UNKNOWN_FIELD,CASEDN,NEXT_NUMBER,INTERVAL_FIELD,
12            BIT_FIELD, TIMESTAMP_OLD_FIELD, CAPITALIZE, BLOB_FIELD,
13            TIMESTAMP_DN_FIELD, TIMESTAMP_UN_FIELD, TIMESTAMP_DNUN_FIELD,
14            GENERATED_FIELD= 128 };

field->length、field->unireg_check、field->pack_flag 主要在 sql/field.cc 文件的 prepare_create_field() 方法中赋值,也有少数字段(如 timestamp)在 Create_field::init() 中赋值

field->charset->number = 33,表示 utf8_general_ci

field->sql_type 枚举值定义:

 1// sql/binary_log_types.h:50
 2typedef enum enum_field_types {
 3  MYSQL_TYPE_DECIMAL, MYSQL_TYPE_TINY,
 4  MYSQL_TYPE_SHORT,  MYSQL_TYPE_LONG,
 5  MYSQL_TYPE_FLOAT,  MYSQL_TYPE_DOUBLE,
 6  MYSQL_TYPE_NULL,   MYSQL_TYPE_TIMESTAMP,
 7  MYSQL_TYPE_LONGLONG,MYSQL_TYPE_INT24,
 8  MYSQL_TYPE_DATE,   MYSQL_TYPE_TIME,
 9  MYSQL_TYPE_DATETIME, MYSQL_TYPE_YEAR,
10  MYSQL_TYPE_NEWDATE, MYSQL_TYPE_VARCHAR,
11  MYSQL_TYPE_BIT,
12  MYSQL_TYPE_TIMESTAMP2,
13  MYSQL_TYPE_DATETIME2,
14  MYSQL_TYPE_TIME2,
15  MYSQL_TYPE_JSON=245,
16  MYSQL_TYPE_NEWDECIMAL=246,
17  MYSQL_TYPE_ENUM=247,
18  MYSQL_TYPE_SET=248,
19  MYSQL_TYPE_TINY_BLOB=249,
20  MYSQL_TYPE_MEDIUM_BLOB=250,
21  MYSQL_TYPE_LONG_BLOB=251,
22  MYSQL_TYPE_BLOB=252,
23  MYSQL_TYPE_VAR_STRING=253,
24  MYSQL_TYPE_STRING=254,
25  MYSQL_TYPE_GEOMETRY=255
26} enum_field_types;

enum、set 选项分隔符:TODO




欢迎扫码关注公众号,我们一起学习更多 MySQL 知识: