[转帖]如何在修改mysql代码添加新SQL命令 _MySQL, Oracle及数据库讨论区_Weblogic技术|Tuxedo技术|中间件技术|Oracle论坛|JAVA论坛|Linux/Unix技术|hadoop论坛_联动北方技术论坛  
网站首页 | 关于我们 | 服务中心 | 经验交流 | 公司荣誉 | 成功案例 | 合作伙伴 | 联系我们 |
联动北方-国内领先的云技术服务提供商
»  游客             当前位置:  论坛首页 »  自由讨论区 »  MySQL, Oracle及数据库讨论区 »
总帖数
1
每页帖数
101/1页1
返回列表
0
发起投票  发起投票 发新帖子
查看: 2244 | 回复: 0   主题: [转帖]如何在修改mysql代码添加新SQL命令         上一篇   下一篇 
ad222888
注册用户
等级:新兵
经验:66
发帖:134
精华:0
注册:2016-9-25
状态:离线
发送短消息息给ad222888 加好友    发送短消息息给ad222888 发消息
发表于: IP:您无权察看 2019-9-3 16:10:15 | [全部帖] [楼主帖] 楼主

本文主要介绍如何在mysql中添加一条新SQL命令, 例如 DISPATCH ADD "gao", 这条命令会去检查参数的值是否为“gao”, 如果是的话 就把全局变量node_type设为1 (默认为0).

注:关于全局变量node_type 请查看我的另一篇文章<<mysql 添加新启动选项>> : http://hi.baidu.com/gao1738/blog/item/84ff8cde9f8f221f92457ec6.html .

说明: 这里要添加的命令本身没太大意义,主要就是介绍一下mysql的命令解析机制和如何添加命令。

关于mysql如何解析命令可以先阅读下这个链接: http://wenku.baidu.ocom/view/3bb97a1dc5da50e2524d7ff7.html 。

1. 概述

mysql主要是用 lex+yacc 的方式进行命令构建与分析的

我们首先要做的就是在lex和yacc的代码文件中添加新命令的 字符定义与语法定义

然后我们要让mysql认识我们的新命令

最后添加与新命令对应的处理代码

命令解析大体上的流程是:

【1.判断命令类型】->【2.根据类型对命令进行分发,分发到对应解析程序】->【3.对命令解析】->【4.根据具体的命令进行分发,分发到对应的命令处理程序】->【5. 处理命令】

第一步对应的代码在 sql/sql_parse.cc的do_command中。 (目前只碰到过COM_QUERY的类型,还不清楚命令的类型在哪里的决定的,以后补充。补:所有从客户端发送的sql语句都是COM_QUERY类型的,这个写死在代码里面了,具体的代码在sql-common/client.c中的mysql_send_query函数中。

第二步对应的代码在同一个文件的dispatch_command中。

第三步对应的代码在同文件的sql_parse中。

第四步对应的代码在同文件的mysql_execute_command中。

最后一步对应的代码就不一定了,看具体的命令而定。

2. 在lex和yacc的代码文件中添加新命令的 字符定义与语法定义

修改的文件:

sql/lex.h
sql/sql_yacc.yy


在sql_yacc.yy中添加 新的字符定义 (大约1400行)找到一列的%token定义的地方,在那里添加如下定义:

%token DISPATCH
然后(大约1640行)找到 %type <NONE> 在最后添加上DISPATCH, 如下:
%type <NONE>
'-' '+' '*' '/' '%' '(' ')'sql_yacc.h
',' '!' '{' '}' '&' ' ' AND_SYM OR_SYM OR_OR_SYM BETWEEN_SYM CASE_SYM
THEN_SYM WHEN_SYM DIV_SYM MOD_SYM OR2_SYM AND_AND_SYM DELETE_SYM DISPATCH


在lex.h在中找到 "static SYMBOL symbols[]"在它的末尾(大约624行)添加如下代码:

{ "DISPATCH", SYM(DISPATCH)}


注:


在sql/sql_yacc.yy中 通过“%union” 定义了在语法解析过程中 用户输入语句块的可选类型。 例如“int num”表示所有num类型的参数都是整形。 字符型的大都返回的都是LEX_STRING类型, 这个类型在include/m_string.h中定义。

用户可以自己定义类型, 下面是一个例子:

1. 在%union中添加 新类型:

Exec_node *e_node;

2. 定义使用这个类型的语句块:

%type <e_node> name_ip_port

3. 解析语句时使用这个类型:

+dispatch_param:
+ ADD NODE_SYM name_ip_port
+ {
+ LEX *lex= Lex;
+ lex->sql_command= SQLCOM_ADD_DISPATCH_NODES;
+ Lex->execution_node= $3; //这里$3指的是 name_ip_port 这个语句块
+ };

4. 在解析类型对应语句块时创建类型对象:

+name_ip_port:
+ TEXT_STRING_sys TEXT_STRING_sys TEXT_STRING_sys
+ {
+ THD *thd= YYTHD;
+ if (!($$= (Exec_node*)thd->alloc(sizeof(Exec_node)))) //alloc是mysql提供的函数,用于在lex中分配空间
+ MYSQL_YYABORT;
+
+ strcpy($$->name, $1.str);
+ strcpy($$->address, $2.str); //$$表示这个语句块要返回的对象
+ $$->port= atoi($3.str);
+ }
+ ;


在sql_yacc.h中找到"enum yytokentype" 在它的末尾添加上dispatch: (这个文件似乎是根据sql/sql_yacc.yy自动生成的,无需自己手动添加)

DISPATCH = 854


和对应的#define DISPATCH 854

(以上忽略)

修改文件:sql/sql_yacc.yy

在sql_yacc.yy中找到 语法定义 “statement:" (大约1723行) (它是语法query的一部分)

在其中添加上 一个语法分支“dispatch”, 例如:

statement:
......
truncate
uninstall
unlock
update
use
xa
dispatch
;


然后在其后添加 dispatch的具体语法:(这里除了定义了DISPATCH ADD 还定义了 DISPATCH DELETE, 但后面的例子只用到DISPATCH ADD)

dispatch:
DISPATCH add_delete;
add_delete:
dispatch_add|dispatch_delete;
dispatch_add:
ADD ident // ident在其他地方定义了, 主要用来表示 字符(例如用于名字的)
{
      THD *thd= YYTHD;
      LEX *lex= thd->lex;
      lex->sql_command= SQLCOM_DISPATCH_ADD; //这里设置了命令标识, 这个标识的定义在下一步中会介绍
      char *tmp=(char*) thd->alloc(sizeof(char)); //这里需要为变量字符在lex的存储空间中分配一个空间来存变量值
      strcpy(tmp, $2.str); // $2表示命令的第2个部分,这里是指"ADD indent"中的indent
      lex->dispatch_message=tmp; //lex->dispatch_message是在lex结构中新定义的,后面会介绍
};
dispatch_delete:
DELETE_SYM ident
{
      THD *thd= YYTHD;
      LEX *lex= thd->lex;
      lex->sql_command= SQLCOM_DISPATCH_DELETE;
      lex->dispatch_message= $2.str;
}
;


注: 这里需要注意避免定义的语法有2义性, 这会导致一个编译错误:“sql/sql_yacc.yy:expected 164 shift/reduce conflicts”

3. 让mysql认识我们的新命令

修改文件:sql/sql_cmd.h

sql/mysqld.cc


sql_cmd.h找到"enum enum_sql_command"(大约88行)中添加如下代码:

SQLCOM_DISPATCH_ADD, SQLCOM_DISPATCH_DELETE


这就是2个新命令的标识。它们在上一步的语法定义中被使用。

然后在mysqld.cc中大约3093行找到 "SHOW_VAR com_status_vars[]"在它的最后一项前添加如下代码:

{"dispatch_add", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DISPATCH_ADD]),SHOW_LONG_STATUS},
{"dispatch_delete", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DISPATCH_DELETE]),SHOW_LONG_STATUS},
{NullS, NullS, SHOW_LONG}
};


注:以上在mysqld.cc中的修改主要是为了能通过mysql非常严格的编译脚本,否则无法通过mysqld.cc中大约3259行的断言compile_time_assert,关于其中的意义现在还不是很了解,以后补充。

3.添加与新命令对应的处理代码

修改文件: sql/sql_lex.h

在sql_lex.h中找到“struct LEX”的定义(大约2054行)然后在其中添加如下代码:

char* x509_subject,*x509_issuer,*ssl_cipher;
String *wild;
char *dispatch_message;
sql_exchange *exchange;
select_result *result;


这里定义的“dispatch_message” 就是用来存储命令的参数值的。该变量在第一步中的语法定义中被使用。

修改文件:sql/sql_parse.cc, 可能还有其他的,看具体需求

在sql_parse.cc中找到 “mysql_execute_command” 函数, 在其中有个很大的swith. 在这个swith中添加我们新命令对应的case:

case SQLCOM_DISPATCH_ADD:
case SQLCOM_DISPATCH_DELETE:
{
      if (!strcmp(lex->dispatch_message, "gao"))
      {
            global_system_variables.node_type=1;
            my_ok(thd); //这句代码设置查询的返回状态为ok 这很重要,否则无法通过mysql在
            //查询对应的状态由thd->stmt_da->status()查看
            //该值将会在sql/Protocol的end_statement中进行判断处理,如果为空的话
            //将导致DBUG_ASSERT(0);
            //注:这里如果要添加验证并做对应的错误处理,可以使用如下语句:
            // my_error(ER_WRONG_ARGUMENTS, MYF(0), "something you want to say in the error");
            //“ER_WRONG_ARGUMENTS” 这个是错误类型, 在include/mysqld_error.h中定义的。 这个错误类型可以在测试用例中用--error捕捉,例如:
            //--error ER_WRONG_ARGUMENTS
            //DISPATCH ADD node_test;
            //如果测试用例在执行“DISPATCH ADD node_test;” 时产生“ER_WRONG_ARGUMENTS ” 错误的话, 这个错误会被捕捉,并将预定义的
            //错误信息打印。 使用这种方式可以在测试用例中添加错误测试,并设置预期要捕捉的错误
            //“MYF(0)” 是把0转成int类型, 这个宏在include/my_global.h中定义, 这个值如果设置成1 的话,mysql会向标准错误输出中输入/007, 即
            //一个错误提示音。 相关的代码处理在mysys/my_mess.c的24行,函数my_message_stderr中。
      }
      else
      my_eof(thd);
      break;
}


4. 对应的测试用例

在mysql-test/t 下面建立一个测试用例文件gao.test, 内容如下:

DISPATCH ADD gao;
show variables like 'node_type';


然后在mysql-test/目录下执行

./mtr gao


结果如下:

==============================================================================
TEST RESULT TIME (ms) or COMMENT
--------------------------------------------------------------------------
worker[1] Using MTR_BUILD_THREAD 300, with reserved ports 13000..13009
DISPATCH ADD gao;
show variables like 'node_type';
Variable_name Value
node_type 1
main.gao [ pass ] 3




赞(0)    操作        顶端 
总帖数
1
每页帖数
101/1页1
返回列表
发新帖子
请输入验证码: 点击刷新验证码
您需要登录后才可以回帖 登录 | 注册
技术讨论