详解MySQL Server端如何发送结果集给客户端
来源:互联网 发布:端口镜像 流量分析 编辑:程序博客网 时间:2024/06/10 23:45
MySQL Server和Client之间的交互有一套定义得很明确的协议,称为MySQL Client/Server Protocol。 写数据库的人,只需要遵循这套协议来写程序,就能让自己的数据库被各种MySQL客户端连接,如mysql命令行,php mysql,JDBC等等。这是一个非常诱人的设计选择(Design Choice)!如果自己实现一套协议,写完数据库后,还需要给各种语言写客户端库,写各种客户端软件,完全就是噩梦。
MySQL源码中,哪里负责实现这个协议呢?这里:
sql/protocol.cc
对于一个select语句,他会有很多行结果,每一行结果都是调用
bool Protocol::send_result_set_row(List<Item> *row_items)
来发送,它的详细实现如下:
/** Send one result set row. @param row_items a collection of column values for that row @return Error status. @retval TRUE Error. @retval FALSE Success.*/bool Protocol::send_result_set_row(List<Item> *row_items){ char buffer[MAX_FIELD_WIDTH]; String str_buffer(buffer, sizeof (buffer), &my_charset_bin); List_iterator_fast<Item> it(*row_items); DBUG_ENTER("Protocol::send_result_set_row"); for (Item *item= it++; item; item= it++) { if (item->send(this, &str_buffer)) { // If we're out of memory, reclaim some, to help us recover. this->free(); DBUG_RETURN(TRUE); } /* Item::send() may generate an error. If so, abort the loop. */ if (thd->is_error()) DBUG_RETURN(TRUE); /* Reset str_buffer to its original state, as it may have been altered in Item::send(). */ str_buffer.set(buffer, sizeof(buffer), &my_charset_bin); } DBUG_RETURN(FALSE);}
一行数据中有很多列,每一列都是一个Item对象,它有一个send方法,负责将Item中的数据按照MySQL Client/Server协议“序列化”到发送缓冲区内:
item->send(this, &str_buffer)
Item是一个基类,它下面有很多子类,子类下面还有子类。如下图1,显示了第一层子类 (图片由Doxgen自动生成), 图2、3是部分细节展开。
如有必要,任何子类都可以去实现send方法。MySQL中,如下一个类实现了send,其中Item::send是兜底方案。
virtual bool Item::send(Protocol *protocol, String *str);inline bool Item_sp_variable::send(Protocol *protocol, String *str);bool Item_name_const::send(Protocol *protocol, String *str)bool Item_field::send(Protocol *protocol, String *str_arg);bool Item_null::send(Protocol *protocol, String *str);bool Item_ref::send(Protocol *prot, String *tmp);bool Item_func_set_user_var::send(Protocol *protocol, String *str_arg);
Field和Item之间如何建立联系的呢?Item_field类!
Item_field(Field *field); // 会将field保存到Item_field::result_field
它将Field封装为一个Item,然后通过下面的代码实现结果的桥接:
bool Item_field::send(Protocol *protocol, String *buffer){ return protocol->store(result_field);}
进而调用
bool Protocol_text::store(Field *field){ if (field->is_null()) return store_null(); char buff[MAX_FIELD_WIDTH]; String str(buff,sizeof(buff), &my_charset_bin); const CHARSET_INFO *tocs= this->thd->variables.character_set_results; field->val_str(&str); /// bridge point return store_string_aux(str.ptr(), str.length(), str.charset(), tocs);}
例如,Field是一个Field_medium,则调用下面的代码:
String *Field_medium::val_str(String *val_buffer, String *val_ptr __attribute__((unused))){ ASSERT_COLUMN_MARKED_FOR_READ; const CHARSET_INFO *cs= &my_charset_numeric; uint length; uint mlength=max(field_length+1,10*cs->mbmaxlen); val_buffer->alloc(mlength); char *to=(char*) val_buffer->ptr(); long j= unsigned_flag ? (long) uint3korr(ptr) : sint3korr(ptr); length=(uint) cs->cset->long10_to_str(cs,to,mlength,-10,j); val_buffer->length(length); if (zerofill) prepend_zeros(val_buffer); /* 他们这个补0做得比较挫,val_buffer是个临时缓冲区,并且还会有memmove操作在里面 */ val_buffer->set_charset(cs); return val_buffer;}
这里会将数值转化成字符串,如果有zerofill标记,还会根据需要在字符串前面补0。
[TBC/未完]
1 0
- 详解MySQL Server端如何发送结果集给客户端
- Redis服务器如何发送回复内容给客户端
- Python编写的客户端给服务器发送指令执行相应的命令并返回结果
- Python编写的客户端给服务器发送指令执行相应的命令并返回结果
- 详解SQL Server如何链接远程MySQL
- Nodejs socket.io 实现私聊:如何给指定客户端发送消息,不是群广播
- Nodejs socket.io 实现私聊:如何给指定客户端发送消息,不是群广播
- mysql简易安装server端+客户端
- MySQL explain结果详解
- Java客户端给服务器发送文件
- 如何给线程发送消息
- 客户端将手机号发送给服务器,服务器将包含该手机号文件名发送给客户端
- mysql命令行客户端结果分页浏览
- Mysql 给查询结果标序号
- 给mysql查询结果添加序号
- CAS SERVER返回更多用户信息给客户端
- TPCC-MySQL输出结果详解
- mysql:如何合并两个查询的结果集的数据
- 20151119 parameter
- 物联网安全思考
- 友盟分享统计
- 让终端窗口“下雪”的有趣指令
- 字符数组三种写法
- 详解MySQL Server端如何发送结果集给客户端
- Sphinx相关资料
- 如何把自己的插件发布到bower平台
- 114 CoreData基本使用
- lightoj 1236 pairs of lcm
- 数据结构与基本运算
- anomaly detection ng recommender system
- Android Studio1.4.x JNI开发基础-基本环境配置
- dijkstra 昂贵的聘礼 poj1062