Mysql Resultset 解析记录

Mysql Resultset 解析记录

  • 结果集消息头
  • 字段定义
  • 结果数据
  • 完整spicy文件

结果集消息头

消息头由消息体长度+消息序列号+消息体组成;消息头长度为3字节,消息序列号长度为1字节。
结果集的消息头消息体内容为结果集的列数。

结果集消息头的spicy1格式如下:

type header = unit {osize : uint8[3];seq : uint8;on %done {self.size = self.osize[2];self.size = self.size << 8;self.size = self.size + self.osize[1];self.size = self.size << 8;self.size = self.size + self.osize[0];}var size : uint32;
};

消息体的内容是结果集的列数,是一个整数;但是为了适配整数的范围,该参数采用了INT_ENC的表现形式,其定义格式如下:

type INT_ENC = unit {osize : uint8;i2 : uint16  &byte-order=spicy::ByteOrder::Little if ((self.osize & 0xff) == 252);i3 : uint8[3] if ((self.osize & 0xff) == 253); i8 : uint64  &byte-order=spicy::ByteOrder::Little if ((self.osize & 0xff) == 254);inull : uint8[0] if ((self.osize & 0xff) == 251); on osize {self.value = self.osize;}on i2 {self.value = self.i2;}on i3 {self.value = self.i3[2];self.value = self.value << 8;self.value = self.value +  self.i3[1];self.value = self.value << 8;self.value = self.value +  self.i3[0];}on i8 {self.value = self.i8;}on inull {self.value = 0;}var value : uint64;
};

从以上定义可知,当列数小于251时,该类型数据占用的字节即为1字节;但是更大后,会采用大于一个字节的方式进行处理;当中有一个特殊情况,当暂用一个字节,且值为251时,表示的是一个无效值;(后面再定义结果集内容时会用到这个值
对于消息头的读取可以进行组合如下:

type COLUMN_SIZE  = unit(inout rs: mysql_rs) {size : INT_ENC { rs.column_size = self.size.value; }
};
public type mysql_rs = unit {head : header;hdata : bytes &size=self.head.size { self.s_col_size.write($$); }on %init {self.s_col_size.connect(new COLUMN_SIZE(self));}var column_size : uint64;sink s_col_size;

在组合中,用到了unit的参数传递了mysql_rs,同时采用了sink的方式对数据进行了一次传递;如此做主要是为了适配消息体后续可能得扩展;整体的格式不需要变化太大,只需要针对消息体进行更改即可;同时兼容性也会更强。
紧接着的是字段定义。

字段定义

每个字段的定义包括,字段头+字段体,字段头的定义与前面的header定义相同,而后定义的是字段的各个内容,包括catalog、database_name,table_name,orig_table_name,column_name, orig_column_name,字符集索引,字符集长度,列类型,列标识及列精度;其中catalog、database_name,table_name,orig_table_name,column_name, orig_column_name都是数据长度+数据内容的方式进行存储。所以字段的读取定义如下:

type column = unit {catalog_len : INT_ENC;:skip bytes &size=self.catalog_len.value;db_len : INT_ENC;db_name : bytes &size = self.db_len.value;tbl_len : INT_ENC;tbl_name : bytes &size = self.tbl_len.value;otbl_len : INT_ENC;otbl_name : bytes &size = self.otbl_len.value;col_len : INT_ENC;col_name : bytes &size = self.col_len.value;ocol_len : INT_ENC;ocol_name : bytes &size = self.ocol_len.value;: skip int8;collation_idx : int16;coll_len  : int32;col_type : int8;col_flag : int16;col_decimals : int8;:   skip bytes &eod;on %done {print "{database:%s, tbl_name:%s, otbl_name:%s, col_name:%s, ocol_name:%s, collation_idx:%x, col_type:%x, col_flag:%x, col_decimals:%x}" %(self.db_name, self.tbl_name, self.otbl_name, self.col_name, self.ocol_name, self.collation_idx, self.col_type, self.col_flag, self.col_decimals);}
};

其中因为catalog的内容定义恒为def,所以通过skip方式进行了忽略。同时其中col_flag的读取字段可能会是1字节也可能是2字节(会根据认证过程中包含的客户端的参数进行变换、此处为了简化直接定义成了2字节);
包含文件头的定义为:
type column_with_header = unit {
head :header;
data : bytes &size=self.head.size { self.b.write($$); }

on %init {self.b.connect(new column);
}sink b;

};

因为在前面的解析总,已经获取了字段数,所以需要将该结构定义成数组的形式

public type mysql_rs = unit {head : header;hdata : bytes &size=self.head.size { self.s_col_size.write($$); }columns : column_with_header[self.column_size ];on %init {self.s_col_size.connect(new COLUMN_SIZE(self));}var column_size : uint64;sink s_col_size;

定义完字段后,接下来接收的就是实际的结果数据了

结果数据

resultset的结果数据以每行的形式进行传输。
每行的开头是header结构体,后面的数据内容即为一行数据,由N(N为结果集的列数)个数据单元组成,每个数据单元的组成形式为INT_ENC+数据实体组成。其定义如下:

type element_value = unit(inout r: row) {size : INT_ENC;data : bytes &size = self.size.value;on %done {print "idx: (%d, %d), size:%d, values:%s" % (r.row_idx, r.col_idx, self.size.value, self.data);}
};

此处为了方便的识别当前元素所处的位置,将行列索引进行了输出。
此处对element_value实际的值为NULL、空字符串的差异进行简要的说明;如果为空字符串,则INT_ENC内容为0,表示长度为0;而如果实际值为NULL,正常内容长度也为0,但是不能区分是否为NULL,所以mysql使用了251这个特殊的数字,将元素定义为了NULL。所以为INT_ENC的中,如果返现第一个字节的内容为251,则会将最终的size置为0,同时其结果也是NULL,此处未做特殊处理,实际应用时,可以继续这个条件进行修正。
行数据定义如下:

type row = unit(r_idx: uint32, column_size : uint64) {eles : element_value(self)[column_size] foreach { self.col_idx = self.col_idx + 1; }on %init {self.row_idx = r_idx;self.col_idx = 0;}var row_idx : uint32;var col_idx : uint32;
};

行数据头+行数据的定义如下:

type row_with_head = unit(inout rs: mysql_rs, column_size :uint64) {head : header;data : bytes &size=self.head.size { if ( *self.data.at(0) == 0xfe) {rs.is_done = True;}if (!rs.is_done)self.b.write($$); }on head {print "head size: %d" % self.head.size;}on %init {self.b.connect(new row(rs.row_idx, column_size));}sink b;
};

因为行数据传输的时候,未包含实际的行数信息;所以需要有标识定义何时结束结果集的传输;此处演示我们采用了相对比较简单的方式,即判断数据开始的值为0xfe则认为数据传输截止了(实际上还有数据大小的判断进行组合判断对结果集是否已经完成得判断)。
所以最终结果集的定义如下:
public type mysql_rs = unit {
head : header;
hdata : bytes &size=self.head.size { self.s_col_size.write($$); }
columns : column_with_header[self.column_size ];
rows : row_with_head(self, self.column_size)[] foreach {
if (self.is_done == True) {
stop;
}
self.row_idx = self.row_idx + 1;
}
on %init {
self.is_done = False;
self.row_idx = 0;
self.s_col_size.connect(new COLUMN_SIZE(self));
}

var column_size : uint64;
var is_done : bool;
var row_idx : uint32;
sink s_col_size;

};

完整spicy文件

完整spicy文件内容如下:

module mysql;
import spicy;type INT_ENC = unit {osize : uint8;i2 : uint16  &byte-order=spicy::ByteOrder::Little if ((self.osize & 0xff) == 252);i3 : uint8[3] if ((self.osize & 0xff) == 253); i8 : uint64  &byte-order=spicy::ByteOrder::Little if ((self.osize & 0xff) == 254);inull : uint8[0] if ((self.osize & 0xff) == 251); on osize {self.value = self.osize;}on i2 {self.value = self.i2;}on i3 {self.value = self.i3[2];self.value = self.value << 8;self.value = self.value +  self.i3[1];self.value = self.value << 8;self.value = self.value +  self.i3[0];}on i8 {self.value = self.i8;}on inull {self.value = 0;}var value : uint64;
};type header = unit {osize : uint8[3];seq : uint8;on %done {self.size = self.osize[2];self.size = self.size << 8;self.size = self.size + self.osize[1];self.size = self.size << 8;self.size = self.size + self.osize[0];}var size : uint32;
};type column = unit {catalog_len : INT_ENC;:skip bytes &size=self.catalog_len.value;db_len : INT_ENC;db_name : bytes &size = self.db_len.value;tbl_len : INT_ENC;tbl_name : bytes &size = self.tbl_len.value;otbl_len : INT_ENC;otbl_name : bytes &size = self.otbl_len.value;col_len : INT_ENC;col_name : bytes &size = self.col_len.value;ocol_len : INT_ENC;ocol_name : bytes &size = self.ocol_len.value;: skip int8;collation_idx : int16;coll_len  : int32;col_type : int8;col_flag : int16;col_decimals : int8;:   skip bytes &eod;on %done {print "{database:%s, tbl_name:%s, otbl_name:%s, col_name:%s, ocol_name:%s, collation_idx:%x, col_type:%x, col_flag:%x, col_decimals:%x}" %(self.db_name, self.tbl_name, self.otbl_name, self.col_name, self.ocol_name, self.collation_idx, self.col_type, self.col_flag, self.col_decimals);}
};type column_with_header = unit {head :header;data : bytes &size=self.head.size { self.b.write($$); }on %init {self.b.connect(new column);}sink b;
};type element_value = unit(inout r: row) {size : INT_ENC;data : bytes &size = self.size.value;on %done {print "idx: (%d, %d), size:%d, values:%s" % (r.row_idx, r.col_idx, self.size.value, self.data);}
};type row = unit(r_idx: uint32, column_size : uint64) {eles : element_value(self)[column_size] foreach { self.col_idx = self.col_idx + 1; }on %init {self.row_idx = r_idx;self.col_idx = 0;}var row_idx : uint32;var col_idx : uint32;};type row_with_head = unit(inout rs: mysql_rs, column_size :uint64) {head : header;data : bytes &size=self.head.size { if ( *self.data.at(0) == 0xfe) {rs.is_done = True;}if (!rs.is_done)self.b.write($$); }on head {print "head size: %d" % self.head.size;}on %init {self.b.connect(new row(rs.row_idx, column_size));}sink b;
};type COLUMN_SIZE  = unit(inout rs: mysql_rs) {size : INT_ENC { rs.column_size = self.size.value; }
};public type mysql_rs = unit {head : header;hdata : bytes &size=self.head.size { self.s_col_size.write($$); }columns : column_with_header[self.column_size ];rows : row_with_head(self, self.column_size)[] foreach {if (self.is_done == True) {stop;}self.row_idx = self.row_idx + 1;}on %init {self.is_done = False;self.row_idx = 0;self.s_col_size.connect(new COLUMN_SIZE(self));}var column_size : uint64;var is_done : bool;var row_idx : uint32;sink s_col_size;};

假设文件存储名为mysql_rs.spicy,则可通过spicy-driver mysql_rs.spicy进行语法校验及调测。调测运行可以采用
printf “0x070x000x00…” | xxd -r -p | spicy-driver mysql_rs.spicy
进行调测输出。
其中xxd命令,主要是将16进制的字符串转换为二进制数。


  1. 1:spicy是zeek用于定义协议解析的语言,可参考https://zeek.org ↩︎

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/894062.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

sem_init的概念和使用案例-简洁版

sem_init 是 POSIX 系统中用于初始化一个信号量的函数。在多线程编程中&#xff0c;信号量是一种常用于同步线程对共享资源的访问的机制。 概念 信号量是一个整数变量&#xff0c;可以用来控制对共享资源的访问。它通常用于实现线程同步或进程同步&#xff0c;以确保在某一时…

SSM开发(七) MyBatis解决实体类(model)的字段名和数据库表的列名不一致方法总结(四种方法)

目录 方法一: 使用@Results和@Result注解(注解方式) 方法二:修改 SQL 查询语句中的别名(注解方式) 方法三: 全局配置别名或结果映射(resultMap,XML配置方式) 方法四:使用@Column注解 在MyBatis中,如果你希望使用注解的方式来操作数据库,但又遇到实体类中的…

AboutDialog组件的功能和用法

文章目录 1 概念介绍2 使用方法3 示例代码 我们在上一章回中介绍了AlertDialog Widget相关的内容,本章回中将介绍AboutDialog Widget.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1 概念介绍 我们在这里说的AboutDialog是一种弹出式窗口&#xff0c;和上一章回中介绍的Al…

设计模式的艺术-策略模式

行为型模式的名称、定义、学习难度和使用频率如下表所示&#xff1a; 1.如何理解策略模式 在策略模式中&#xff0c;可以定义一些独立的类来封装不同的算法&#xff0c;每个类封装一种具体的算法。在这里&#xff0c;每个封装算法的类都可以称之为一种策略&#xff08;Strategy…

软件架构的演变:从大型机和整体式应用到分布式计算

注&#xff1a;本文为 “软件架构演变” 相关文章合辑。 英文引文机翻&#xff0c;未校。 Evolution of Software Architecture: From Mainframes and Monoliths to Distributed Computing Liv Wong Technical Writer August 06, 2024 Software architecture—the blueprint…

SET alter system reload

目录标题 alter system 只是 写 auto 文件SET & alter system1. **会话级别参数&#xff08;Session-level parameters&#xff09;**2. **系统级别参数&#xff08;System-level parameters&#xff09;**3. **某些特定的超级用户参数**4. **修改时生效的参数**总结&#…

量子编程语言:Qiskit 与 Cirq

在量子计算的领域&#xff0c;开发者已经可以使用一些专门为量子计算设计的编程语言和框架。其中&#xff0c;Qiskit 和 Cirq 是两个非常流行的选择&#xff0c;它们为不同的量子计算机提供编程接口&#xff0c;帮助开发者理解量子电路的设计和执行。 1. Qiskit Qiskit 是 IBM…

Java教程练习:学生信息管理系统

文章目录 学生管理系统1、需求文档需求分析 2、新建学生实体类3、实现基本菜单和退出功能4、查询和添加4.1 查询学生信息4.2 添加学生信息 5、修改和删除5.1 删除功能实现5.2 修改功能实现 完整代码下载 学生管理系统 1、需求文档 需求 采取控制台的方式书写学生管理系统。 …

Cocoa和Cocoa Touch是什么语言写成的?什么是Cocoa?编程语言中什么是框架?为什么苹果公司Cocoa类库有不少NS前缀?Swift编程语言?

Cocoa和Cocoa Touch是什么语言写成的? 二者主要都是用Objective-C语言编写而成的。 什么是Cocoa? Cocoa是苹果操作系统macOS和iOS上的应用程序开发框架集合&#xff0c;核心语言是Objective-C编程语言&#xff0c;在移动平台被称为Cocoa Touch&#xff0c;Cocoa包含多个子框架…

汽车蓝牙钥匙定位仿真小程序

此需求来自于粉丝的真实需求,假期没事,牛刀小试。 一、项目背景 如今,智能车钥匙和移动端定位技术已经相当普及。为了探索蓝牙 Beacon 在短距离定位场景下的可行性,我们搭建了一个简易原型:利用 UniApp 在移动端采集蓝牙信标的 RSSI(信号强度),通过三边定位算法估算钥…

【Docker】Docker入门了解

文章目录 Docker 的核心概念Docker 常用命令示例&#xff1a;构建一个简单的 C 应用容器1. 创建 C 应用2. 创建 Dockerfile3. 构建镜像4. 运行容器 Docker 优势学习 Docker 的下一步 **一、Docker 是什么&#xff1f;****为什么 C 开发者需要 Docker&#xff1f;** **二、核心概…

【C++ 真题】P1706 全排列问题

全排列问题 题目描述 按照字典序输出自然数 1 1 1 到 n n n 所有不重复的排列&#xff0c;即 n n n 的全排列&#xff0c;要求所产生的任一数字序列中不允许出现重复的数字。 输入格式 一个整数 n n n。 输出格式 由 1 ∼ n 1 \sim n 1∼n 组成的所有不重复的数字序…

Acwing92递归实现指针型枚举

从 1∼n1∼n 这 nn 个整数中随机选取任意多个&#xff0c;输出所有可能的选择方案。 输入格式 输入一个整数 nn。 输出格式 每行输出一种方案。 同一行内的数必须升序排列&#xff0c;相邻两个数用恰好 11 个空格隔开。 对于没有选任何数的方案&#xff0c;输出空行。 本…

新项目上传gitlab

Git global setup git config --global user.name “FUFANGYU” git config --global user.email “fyfucnic.cn” Create a new repository git clone gitgit.dev.arp.cn:casDs/sawrd.git cd sawrd touch README.md git add README.md git commit -m “add README” git push…

崇州市街子古镇正月初一繁华剪影

今天是蛇年正月初一&#xff0c;下午笔者步出家门&#xff0c;逛到了崇州市街子古镇井水街&#xff0c;想看看景象如何。结果看到的是车水马龙、人流如织&#xff0c;繁花似锦&#xff0c;热闹非凡&#xff0c;原来今天开始预订此地摆下的长街宴。心里高兴&#xff0c;便用手机…

Java设计模式:结构型模式→组合模式

Java 组合模式详解 1. 定义 组合模式&#xff08;Composite Pattern&#xff09;是一种结构型设计模式&#xff0c;它允许将对象组合成树形结构以表示“部分-整体”的层次。组合模式使得客户端能够以统一的方式对待单个对象和对象集合的一致性&#xff0c;有助于处理树形结构…

【性能优化专题系列】利用CompletableFuture优化多接口调用场景下的性能

背景说明 在实际的软件开发中&#xff0c;我们经常会遇到需要批量调用接口的场景。例如&#xff0c;电商系统在生成商品详情页时&#xff0c;需要同时调用多个服务接口来获取商品的基本信息、库存信息、价格信息、用户评价等。 传统的依次调用方式存在性能问题 面对上述场景…

多线程-线程池的使用

1. 线程池 1.1 线程状态介绍 当线程被创建并启动以后&#xff0c;它既不是一启动就进入了执行状态&#xff0c;也不是一直处于执行状态。线程对象在不同的时期有不同的状态。那么 Java 中的线程存在哪几种状态呢&#xff1f;Java 中的线程 状态被定义在了 java.lang.Thread.…

JavaSE第十一天——集合框架Collection

一、List接口 List接口是一个有序的集合&#xff0c;允许元素有重复&#xff0c;它继承了Collection接口&#xff0c;提供了许多额外的功能&#xff0c;比如基于索引的插入、删除和访问元素等。 常见的List接口的实现类有ArrayList、LinkedList和Vector。 List接口的实现类 …

JS 正则表达式 -- 分组【详解】含普通分组、命名分组、反向引用

普通分组 使用圆括号 () 来创建分组捕获匹配的内容&#xff0c;通过正则表达式匹配结果的数组来访问这些捕获的内容。 const str "Hello, World!"; const regex /(Hello), (World)!$/; const match str.match(regex);if (match) {console.log("完整匹配结果…