// header
所有的Packet都有一个4字节的header.
前边3字节是packet body的长度, 后边1个字节是packet的编号.
编号在每开始一个新命令时都会重置.
// first packet
当client连接到mysql时,mysql首先回一个greeting packet.
packet body里包含了
server version,
thread id, // connection id
server capabilities,
server charset
等信息.
// login packet
packet body里包含了
client capabilities
max packet
client charset
username
password
等信息.
// Response
response packet body的第一个字节表明了response的类型.
0xff (255): ERROR
0: OK
// Response Error body
2字节的Error Code,
#号跟着5字节的SQL STATE Code,比如用户名密码不对时返回 #28000, Table不存在时返回 #42S02
接下来是具体的出错信息.
// 出错示例:
ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: YES)
ERROR 1146 (42S02): Table 'test.aaa' doesn't exist
// Response OK body
affected rows:
如果影响行数小于251,占一个字节,就是影响的行数.
如果小于65536,占3个字节,第一个字节是0xfc(252),后边跟着两个字节的影响行数
如果小于16777216,占4个字节,第一个字节是0xfd(253),后边跟着三个字节的影响行数
如果影响行数大于等于16777216,第一个字节是0xfe(254),后边跟着8字节的影响行数
last_insert_id: 格式同affected rows
server status: 2个字节
warnings: 2个字节
// Query packet
packet body很简单,1个字节的command code,后边跟着command的参数,如果有的话.
// Result Set packets
第一个packet的body包含了返回结果里列的数目.
比如对select id,title,body from post这样一个query的返回结果,第一个packet body的内容是3.
接下来有多个列,就有多少个packet,这些packet叫 field description packets.
packet body里包含了
数据库名,
表名/原始表名,
列名/原始列名,
该列的charset,
列的长度,
列的类型,
列的flags(not null, primary key, zero fill等),
等信息.
field description packets完了之后跟一个EOF packet表明没有更多列了.
EOF packet的第一个字节是0xfe(254). 后边跟着2字节的warning,2字节的server status.
接下来就是一行一行的数据了,每一行数据占一个packet. packet body里直接就是数据本身.
比如select id, title, body from post的返回结果.
id real length + id +
title real length + title +
body real length + body
这里需要注意的是,id列虽然类型是int,返回结果里还是将其转成了字符串返回了.
最后再跟一个EOF packet表示结束.
这里有个问题, EOF packet的第一个字节是0xfe,
result set packet如果第一列的长度超过16777216的话,也是0xfe,怎么区分呢?
通过packet length区分. EOF packet限制其长度不能超过7个字节.而result set packet的长度在上述情况下远大于7个字节.
额外收获:
1. 在login request里就可以指定数据库和charset.
2. 服务器返回数据时会根据client的charset做转换.但不见得能转换成功.比如server是utf8的,client是gbk的,返回数据时列的charset就unknown了.如果client是latin1(默认charset),服务器返回时列的charset也是latin1,但中文直接就是?了.
2. last_insert_id()其实在insert执行成功时返回的packet里就有,所以pdo->lastInsertId()根本就没有再次向mysql发请求.
3. update时的affected rows默认返回的是真的update了几行,也可以设置返回找到了几行. 实际是不管有没有设置,返回的packet里有message说明matched多少,changed的多少,只不过是字符串形式的,不太好提取.