MySQL的语法与标准SQL语法相同,默认使用分号“;”作为一条SQL语句结束的标志。且可以使用delimiter命令将其修改成其他符号,如:“delimiter |”。在MySQL看来所有的语句可以分为两类,一类是SQL Statement(SQL语句),一类是Command Statement(系统命令)。
系统命令无需在语句结尾处添加分号直接回车即可,通常情况下长格式的系统命令在行尾添加分号不会影响命令的正常执行,如:use test;。习惯了每敲一条命令都打一个分号的管理员和程序员们现在注意了,分号不要随便敲,MySQL在这里给我们挖了一个坑。
让我们来看一个案例:
使用mysqldump命令导出一个名为"pp#idhuishou"的表,mysqldump -uroot -p test pp#idhuishou -r pp#idhuishou.sql,文件名与表名相同都含有一个井号。 使用SOURCE命令再导入数据的时候,我又习惯性的在结尾多打了一个分号,结果敲完分号再敲回车语句不执行,如果不敲分号,语句正常执行,数据被成功灌入,显然是客户端把井号后的内容当成注释来处理了。
mysql> source pp#idhuishou.sql;
->
变通解决办法:
使用输入重定向的方式导入mysql -uroot -p test < pp#idhuishou.sql
将文件重命名再SOURCE,不包含井号就不会受分号影响mv pp#idhuishou.sql idhuishou.sql
让我们分析一下MySQL倒底搞了什么鬼,MySQL处理一条语句首先要判断这条语句是否为系统命令,如果是系统命令会有单独的函数单独处理,如:com_warnings,com_use,com_source,com_help等。
Code:
static int read_and_execute(bool in interactive) {
for(;;) {
... ...
if ((named_cmds || glob_buffer.is_empty()) && !ml_comment && !in_string && (com=find_command(line,0)))
//find_command函数用来判断一条语句是否为系统命令 {
if ((*com->func)(&glob_buffer,line) > 0)
//*com->func为一个函数指针,分别指向不同命令的函数,如com_warnings,com_use,com_source,com_help等 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)) //如果语句不是系统命令就会走到这里
break;
}
/* if in batch mode, send last query even if it doesn't end with \g or go */
if (!interactive && !status.exit_status)
{
remove_cntrl(glob_buffer);
if (!glob_buffer.is_empty())
{
status.exit_status=1;
if (com_go(&glob_buffer,line) <= 0)
status.exit_status=0;
}
}
... ...
}
(未完待续)