A-A+

MySQL client模块源码逻辑

2014年04月13日 TenDB 暂无评论

MySQL的client是与server进行交互的模块,下面从源码的角度对client的处理逻辑进行讨论。

client的入口是client/mysql.cc中的main函数。主要有如下函数:

MY_INIT(),初始化一些系统函数、资源及变量,比如线程、临界区及tcp/ip等;

Isatty(),判定输入输出是文件还是console

load_defaults(),从配置文件读取配置参数;

get_options(),读取mysql 选项参数;

batch_readline_init(),初始化console大小;

mysql_server_init(),与服务器相关信息的初始化;

init_alloc_root(),分配root内存;

sql_connect(),与server连接;

read_and_execute(),处理SQL语句并与server交互;

mysql_end(),资源释放

其中最主要的函数就是read_and_execute(),在这个函数中有对SQL语句的完整处理。

read_and_execute()函数的主体是一个大的for(;;)循环。在这个循环里,处理每一条读取或者输入的字符串,分别通过函数batch_readline()从文件读或函数my_cgets()从终端读,利用哪种方式读取取决于前面的isatty()函数值。

在读取一行字符串后,需要对其进行处理。首先,若满足当前串启动命名命令(named_cmds)或者在当前串前面的串已被处理(glob_buffer为空),并用当前内容不在字符串(指指引号)里面用有对应的find_command()类型,则进行处理;否则,则直接调用后续的add_line函数处理当前串。其中add_line()是字符串处理的最主要函数。对应示例代码如下:

if ((named_cmds || glob_buffer.is_empty())&& !ml_comment &&

!in_string && (com=find_command(line,0)))

{

if ((*com->func)(&glob_buffer,line) > 0)

break;

if (glob_buffer.is_empty()) // If buffer was emptied

in_string=0;

#ifdef HAVE_READLINE

if (interactive && status.add_to_history && not_in_history(line))

add_history(line);

#endif

continue;

}

if (add_line(glob_buffer, line, &in_string, &ml_comment,status.line_buff ? status.line_buff->truncated : 0))

break;

其中利用find_command()获取SQL语句有如下图所示的类型,若上述代码中能够返回下面的com类型,则利用代码函数指针(*com->func)(&glob_buffer,line)来对应的调用下面函数。比如处理delimiter、help、clear、go操作等就会调用下面函数。

clip_image001

函数add_line()主要处理了读取字符串。读取的字符串中,会包含有delimiter标识、关键字、注释(--类型或者#类型)、分号及转义字符等,如何对期进行断句得到目标SQL语句呢?

函数add_line()的主要代码逻辑如下:

for (pos=out=line ; (inchar= (uchar) *pos) ; pos++)

{//遍历整个字符串

if (!preserve_comments)//处理

{对字符串前面的空格类字符进行处理}

if (!*ml_comment && inchar == '\\' &&!(*in_string &&

(mysql.server_status & SERVER_STATUS_NO_BACKSLASH_ESCAPES)))

{主要处理转义字符指定的命令,如\c,\N等}

else if (!*ml_comment && !*in_string && is_prefix(pos, delimiter))

{主要处理针对delimiter的断句}

else if (!*ml_comment && (!*in_string && (inchar == '#' ||(inchar == '-' && pos[1] == '-' &&(my_isspace(charset_info,pos[2]) ||!pos[2])))))

{处理# 与类型的注释}

else if (!*in_string && inchar == '/' && *(pos+1) == '*' &&*(pos+2) != '!')

{处理/*! */类型的条件}

else if (*ml_comment && !ss_comment && inchar == '*' && *(pos + 1) == '/')

{处理/* */类的注释,找注释的结尾*/}

Else

{将当前串加入到buffer中,待继续输入}

}

下面对上述if条件作出解释:

inchar表示当前处理的字符;

preserve_comments标识当前内容是否是注释,位于字符串的开头,主要处理/* */类的多行注释。

ml_comment标识当前处理字符是不是注释;注意,此处的注释指/**/中的注释,比如同一行中的—及#产生的注释则另外处理;

in_string标识当前处理字符在不在字符串(引号或反引号)中;

mysql.server_status & SERVER_STATUS_NO_BACKSLASH_ESCAPES,标识’\’有没有转义的作用,该参数可以在server里被设定;

is_prefix(pos, delimiter),判定以当前字符开始的串,是不是以delimiter串为前缀。

在上述处理中,若当前语句是一个完整的SQL语句的最后一部分(指被delimiter断句了),则交付于com_fun函数进行处理,实质上是交完整的SQL句子交付到server部分进行处理。

利用delimiter断句,client模块可能一并传入多个sql语句到server部分处理,比如:

Delimiter $$

Insert into t1 values(1);

Insert into t1 values(2);

$$

Delimiter ;

在再次遇到$$ 后,两条SQL语句才被发送到server端。若server将上述两条sql当成一条来处理,显然是错误的,client模块在与server连接的时候,可以指定参数CLIENT_MULTI_STATEMENTS来让server部分能够处理多条语句。

原创文章,转载请注明: 转载自腾讯游戏DBA团队

本文链接地址: MySQL client模块源码逻辑

文章的脚注信息由WordPress的wp-posturl插件自动生成

标签:
Copyright © 腾讯游戏DBA团队 保留所有权利.  

用户登录

分享到: