首页 > 数据库 >详解MySQL Server端如何发送结果集给客户端

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

时间:2023-06-15 15:03:31浏览次数:58  
标签:Protocol String buffer 集给 send Server Item str MySQL


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

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

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

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

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


标签:Protocol,String,buffer,集给,send,Server,Item,str,MySQL
From: https://blog.51cto.com/u_16162111/6492529

相关文章

  • CentOs7安装部署Sonar环境(JDK1.8+MySql5.7+sonarqube7.8)
    sonarqube安装前环境准备JDK1.8、MySql5.7。一、JDK安装1、下载jdk#打开下面的网址,选择jdk-8u371-linux-x64.tar.gz进行下载(8u371版本可能会有区别,但是没有影响)http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html2、安装jdk#创建......
  • 云小课|RDS for MySQL参数模板一键导入导出,参数配置轻松搞定
    摘要:云数据库RDSforMySQL支持参数模板的导入和导出功能。本文分享自华为云社区《【云小课】【第56课】RDSforMySQL参数模板一键导入导出,参数配置轻松搞定》,作者:数据库的小云妹。云数据库RDSforMySQL支持参数模板的导入和导出功能。导入参数模板:导入后会生成一个新的参数模板,......
  • 云小课|RDS for MySQL参数模板一键导入导出,参数配置轻松搞定
    摘要:云数据库RDSforMySQL支持参数模板的导入和导出功能。本文分享自华为云社区《【云小课】【第56课】RDSforMySQL参数模板一键导入导出,参数配置轻松搞定》,作者:数据库的小云妹。云数据库RDSforMySQL支持参数模板的导入和导出功能。导入参数模板:导入后会生成一个新的参......
  • mysql和neo4j集成多数据源和事务
    在微服务大行其道的今天,按理说不应该有多数据源这种问题(嗯,主从库算是一个多数据源的很常见的场景。),但是也没人规定不能这样做。就算有人规定的,曾经被奉为圭臬的数据库三大范式现在被宽表冲得七零八落,在很多场景下,其实是鼓励建立冗余字段的。话说项目中需要用到图数据库,我们选用......
  • 基于中间件实现MySQL读写分离
    1.场景在数据库读多写少的时候2.读写分离只在主服务器上写,只在从服务器上读。使用amoeba中间件3.部署3.1Dockercompose一键部署Compose文件:version:'3.8'services:mysql_master:image:mysql:8.0.33-oracle......
  • windows环境下操作,同一台机器 如何跑两个mysql服务?
    在同一台机器上运行两个MySQL服务是可行的,但需要注意以下几点:确保端口号不冲突。每个MySQL服务都需要监听一个独立的端口,以便客户端可以连接到正确的服务。配置文件的区分。每个MySQL实例需要有自己独立的配置文件,包括数据存储路径、日志文件路径等信息。确保资源......
  • 如何从CLI管理阿里巴巴MySQL数据库
    阿里云提供高度可用的按需MySQL,SQLServer和PostgreSQL数据库,作为其ApsaraDBforRDS  (关系数据库服务)的一部分。RDS易于设置和部署。该服务处理数据库服务器的所有管理任务,包括在发生灾难时进行配置,修补和恢复。除了这个即用型数据库服务,您还可以选择从弹性计算服务(ECS)实例设置......
  • MySQL中的非确定性函数(即rand)可能会让您感到惊讶
    使用sysbench处理测试用例,我遇到了这个问题:的MySQL>选择*从sbtest1其中ID=ROUND(RAND()*10000,0);+------+--------+-------------------------------------------------------------------------------------------------------------------------+----------------......
  • MySQL数据库运维实录--通过MySQL Shell Dump/Load 实现数据库对象的逻辑备份与恢复
    [mysql@node01~]$mysqlshMySQLJS>\connectroot@node01:3306MySQLnode01:3306sslJS>util.dumpInstance('/home/mysql/backup')[mysql@node01~]$scp-rbackup/*node02:/home/mysql/backup/util.dumpInstance的关键特性1,多线程备份。并发线程数由thr......
  • mysql8.0创建新用户
     查看当前用户usemysql;selectcurrent_user();或者:selectUser,authentication_string,Hostfromuser;查看权限showgrantsfor'user'@'ip';或者showgrants;创建一个给新用户的数据库createdatabasetestDatabase;创建新用户同时配置给该用户testData......