(五)Go-编写MySQL数据库驱动之MySQL协议解析OK/Error

· 1311字 · 3分钟 · 阅读量

准备:

(一)服务器响应报文 🔗

当MySQL客户端成功连接MySQL服务端时,就可以可服务端进行sql交互了,比如use切换数据库、show展示所有数据库、CURD操作等,大体上分为,MySQL发送sql语句给MySQL服务端(上文中的客户端端请求报文)。MySQL服务端接收到sql语句后进行逻辑处理。处理完成以后MySQL服务端会返回相应的执行结果给客户端,MySQL客户端在收到响应报文后,需要首先检查第1个字节的值,来区分响应报文的类型。响应的数据包分为:OK_PacketError_PacketEOF_PacketResult SetData FieldField_Packet

响应报文类型 第1个字节取值范围
OK 响应报文 0x00
Error 响应报文 0xFF
Result Set 报文 0x01 - 0xFA
Field 报文 0x01 - 0xFA
Row Data 报文 0x01 - 0xFA
EOF 报文 0xFE

注: 响应报文的第1个字节在不同类型中含义不同,比如在OK报文中,该字节并没有实际意义,值恒为0x00;而在Result Set报文中,该字节又是长度编码的二进制数据结构(Length Coded Binary)中的第1字节。

(二)OK Packet 🔗

OK Packet数据格式 🔗

普通的OK包会在以下几种情况下产生,由MySQL服务端发送给相应的接收方:

  • COM_PING: 连接或者测试数据库
  • COM_QUERY: 不需要查询结果集的操作,比如INSERT, UPDATE, ALTER TABLE,DELETE
  • COM_REFRESH: 数据刷新
  • COM_REGISTER_SLAVE: 注册从服务器

首先前面的四个字节是包头,其中前三位代表packet数据长度,第四位是包序列号。以下是OK Packet的四字节后的内容:

MySQL 4.1 及之后的版本:

相对包内容的位置 长度(字节) 名称 描述
0 1 包头标识 0x00 代表这是一个OK 包
1 rows_len 影响行数 相应操作影响的行数,比如一个Update操作的记录是5条,那么这个值就为5
1 + rows_len id_len 自增id 插入一条记录时,如果是自增id的话,返回的id值
1 + rows_len + id_len 2 服务器状态 用于表示服务器状态,比如是否是事务模式或者自动提交模式
3 + rows_len + id_len 2 警告数 上次命令引起的警告数
5 + rows_len + id_len msg_len 额外信息 此次操作的一些额外信息

Go代码实现:

type Success struct {
	Row          uint64
	LastInsertId uint64
	ServerStatus uint16
	Warnings     uint16
	Message      string
}

func (p *Packet) success(packet []byte) *Success {
	sucPacket := &Success{}

	row := make([]byte, 8)
	copy(row, packet[0:1])
	sucPacket.Row = binary.LittleEndian.Uint64(row)

	LastInsertId := make([]byte, 8)
	copy(LastInsertId, packet[1:2])
	sucPacket.LastInsertId = binary.LittleEndian.Uint64(LastInsertId)

	sucPacket.ServerStatus = binary.LittleEndian.Uint16(packet[2:4])

	sucPacket.Warnings = binary.LittleEndian.Uint16(packet[4:6])

	sucPacket.Message = string(packet[6:])

	return sucPacket
}

(三)Error Packet 🔗

Error Packet数据格式 🔗

顾名思义Error 包就是当出现错误的时候返回的信息,比如账户验证不通过,查询命令不合法,非空字段未指定值等相关操作,MySQL服务端都会向MySQL客户端发送Error 包。

首先前面的四个字节是包头,其中前三位代表packet数据长度,第四位是包序列号。以下是Error Packet的四字节后的内容:

相对包内容的位置 长度(字节) 名称 描述
0 1 包头标识 0xFF 代表这是一个Error 包
1 2 错误代码 该错误的相应错误代码
3 1 标识位 SQL执行状态标识位,用’#’进行标识
4 5 执行状态 SQL的具体执行状态
9 msg_len 错误信息 具体的错误信息

Go代码实现:

type Error struct {
	ErrorCode         uint16 //该错误的相应错误代码
	IdentificationBit []byte //标识位	SQL执行状态标识位,用’#’进行标识
	SqlState          string //执行状态	SQL的具体执行状态
	ErrorMessage      string //错误信息	具体的错误信息
}

func (p *Packet) error(packet []byte) *Error {
	errorPacket := &Error{}

	errorPacket.ErrorCode = binary.LittleEndian.Uint16(packet[0:2])

	errorPacket.IdentificationBit = packet[2:3]

	errorPacket.SqlState = string(packet[3:8])

	errorPacket.ErrorMessage = string(packet[8:])
	return errorPacket
}