详解MySQL Server端如何发送结果集给客户端

原创 2015年11月19日 19:06:08

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是部分细节展开。
Item类层次

Item_ident子类层次

常量类结构

如有必要,任何子类都可以去实现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/未完]

版权声明:本文为博主原创文章,未经博主允许不得转载。微信hustos联系博主。

相关文章推荐

以webService为客户端获取List泛型结果集

首先搭建好webService,添加XFire1.2Core Libraries 和XFire1.2HTTP Client Libraries,连接上数据库,下例以oracle为数据库。 连接Oral...

Android TCP的客户端(请求的发送和响应结果接收)

Android TCP客户端发送请求并接收响应 import android.util.Log; import com.changhong.electric_controll.UDPResp...

服务器实时通知客户端方案,服务器发送/推送事件方案(2)server event,附送经典例子

server event是html5规范的一部分,它相比websocket使用起来更简单,对服务器的改动也最小 前端html部分 if(typeof(EventSource)!...
  • kkgbn
  • kkgbn
  • 2016-11-14 15:59
  • 1228

有关 SQLserver + JavaWeb端 多条件结果集 分页显示 的一点总结

三种实现多条件结果集分页的方式,可能还有其他方法

用友--客户科目余额表sql语句:注意两条语句结果集的合并

Select Code.cCode as NaCo1,Code.ccode_name as Name1,Customer.cCusCode as NaCo2,Customer.cCusAbbName ...
  • shj777
  • shj777
  • 2012-03-30 15:52
  • 2544

如何在SQL Server查询语句(Select)中检索存储过程(Store Procedure)的结果集?(

如何在SQL Server查询语句(Select)中检索存储过程(Store Procedure)的结果集?(2006-12-14 09:25:36) 与这个问题具有相同性质的其他描述还...

sql server 查询结果集自动添加编号

SQL提供了一个IDENTITY Function,可以得到标识列的值,不过可惜的很的是,这个函数只能用于SELECT INTO语句,所以我们只好引入一个临时表了 Ex: USE pubs SE...
  • zgyhh
  • zgyhh
  • 2013-04-22 08:59
  • 4399

PHP调用MsSQL Server 2012存储过程获取多结果集(包含output参数)

【PHP Source Code】:$dbh = new PDO('sqlsrv:server=连接地址;Database=数据库名', 用户名, 密码); try { $procName = "P...

Android开发之搜芽客户端Version1.0的结束(过程和结果展示)

本文为原创为:http://blog.csdn.net/minimicall 转载须注明。 一个博士同学,找到我,想我合伙,帮助他解决移动端产品。他给我描述了他的想法,搜布,用图像识别的算法去搜索...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)