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 已经被废弃
- frm header 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_ROWS
、MIN_ROWS
、AVG_ROW_LENGTH
、STATS_AUTO_RECLAC
、STATS_SAMPLE_PAGES
、KEY_BLOCK_SIZE
这几个在建表或建索引时指定的选项的详细说明,见官方文档:CREATE TABLE
以下关于 Offset 04、Offset 08 ~ 09、Offset 10 ~ 13 处的说明都和 make_new_entry() 方法有关,为了内容完整性所以写出来了,对其中细节不兴趣的可以不看
Offset 04
在sql/table.cc:create_frm()
中写入值1
,在sql/table.cc:make_new_entry()
中会修改,是有个计算逻辑算出来的,按照 5.7.35 版本中的逻辑,算出来的值固定是3
,即Offset 04
的内容先被设置为1
,后被修改为3
Offset 08 ~ 09
在sql/table.cc:make_new_entry()
写入值,按照 5.7.35 版本中的逻辑,算出来的值是1
Offset 04
、Offset 08 ~ 09
的值最终都是通过sql/table.cc:make_new_entry()
写入的; 在MariaDB 10.0.2
、MySQL 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 32
:0
表示是大于 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 | |
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_EMPTY 或 field->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 知识: