MySQL的prepare使用及遇到bug解析过程
2022-05-07 17:00:01 来源:易采站长站 作者:
目录
一、问题发现二、问题调查过程三、问题解决方案四、问题总结一、问题发现
在一次开发中使用 MySQL PREPARE 以后,从 prepare 直接取 name 赋值给 lex->prepared_stmt_name 然后给 EXECUTE 用,发现有一定概率找不到 prepare stmt 的 name,于是开始动手调查问题发生的原因。
SQL语句示例:
CREATE TABLE t1 (a INT, b VARCHAR(10)); PREPARE dbms_sql_stmt4 FROM 'INSERT INTO t1 VALUES (1,''11'')'; EXECUTE dbms_sql_stmt4;
报错:
SQL Error [1243] [HY000]: Unknown prepared statement handler (dbms_sql_stmt4??p??]UU) given to EXECUTE
二、问题调查过程
1、根据报错信息找到对应源码,发现在MySQL_sql_stmt_execute里面有判断当找不到 stmt name 时候报错信息。
这里的 name 此时已经是乱码了。
void MySQL_sql_stmt_execute(THD *thd) {
LEX *lex = thd->lex;
const LEX_CSTRING &name = lex->prepared_stmt_name;
DBUG_TRACE;
DBUG_PRINT("info", ("EXECUTE: %.*sn", (int)name.length, name.str));
Prepared_statement *stmt;
if (!(stmt = thd->stmt_map.find_by_name(name))) {
my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), static_cast<int>(name.length),
name.str, "EXECUTE");
return;
}
2、这个 lex->prepared_stmt_name 是从 prepare name 中赋值的,于是调查 prepare 这个 name 设置的函数。
bool Prepared_statement::set_name(const LEX_CSTRING &name_arg) {
m_name.length = name_arg.length;
m_name.str = static_cast<char *>(
memdup_root(m_arena.mem_root, name_arg.str, name_arg.length));
return m_name.str == nullptr;
}
gdb 跟踪代码:
Thread 46 "MySQLd" hit Breakpoint 1, Prepared_statement::set_name (this=0x7fff2cbf3250, name_arg=...)
at /home/wuyy/greatdb/gitmerge/percona-server/sql/sql_prepare.cc:2447
2447 bool Prepared_statement::set_name(const LEX_CSTRING &name_arg) {
(gdb) n
2448 m_name.length = name_arg.length;
(gdb)
2450 memdup_root(m_arena.mem_root, name_arg.str, name_arg.length));
(gdb)
2449 m_name.str = static_cast<char *>(
(gdb)
2451 return m_name.str == nullptr;
(gdb) p m_name
$9 = {
str = 0x7fff2cd09a68 "dbms_sql_stmt4", '217' <repeats 98 times>, "FLOAT",
length = 14
# 可以看到 m_name 后面出现了乱码,说明 m_nam e最后不是 结束,而是别的字符。
3、接着到 execute 的函数看一下这个 name 值,发现确实后面跟的不是
