时间:2022-05-08来源:www.pcxitongcheng.com作者:电脑系统城
在一次开发中使用 MySQL PREPARE 以后,从 prepare 直接取 name 赋值给 lex->prepared_stmt_name 然后给 EXECUTE 用,发现有一定概率找不到 prepare stmt 的 name,于是开始动手调查问题发生的原因。
SQL语句示例:
1 2 3 |
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
此时已经是乱码了。
1 2 3 4 5 6 7 8 9 10 11 |
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: %.*s\n" , ( 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 设置的函数。
1 2 3 4 5 6 |
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 跟踪代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
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最后不是 \0 结束,而是别的字符。 |
3、接着到 execute
的函数看一下这个 name
值,发现确实后面跟的不是 \0
结束符,而是变为乱码。于是这里当然会报错找不到该 stmt name
了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
Thread 46 "MySQLd" hit Breakpoint 2, MySQL_sql_stmt_execute (thd=0x7fff2c002688) at /home/wuyy/greatdb/gitmerge/percona-server/sql/sql_prepare.cc:1944 1944 void MySQL_sql_stmt_execute(THD *thd) { (gdb) n 1945 LEX *lex = thd->lex; (gdb) 1946 const LEX_CSTRING & name = lex->prepared_stmt_name; (gdb) 1947 DBUG_TRACE; (gdb) p name $10 = (const LEX_CSTRING &) @0x7fff2cd501e0: { str = 0x7fff2cd09a68 "dbms_sql_stmt4\217\217p\271\221]UU" , length = 22 } (gdb) n 1948 DBUG_PRINT( "info" , ( "EXECUTE: %.*s\n" , ( int ) name .length, name .str)); (gdb) 1951 if (!(stmt = thd->stmt_map.find_by_name( name ))) { (gdb) 1953 name .str, "EXECUTE" ); (gdb) 1952 my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), static_cast< int >( name .length), (gdb) 1954 return ; # 结果报错了。 |
通过以上 gdb 跟踪过程我们可以发现 prepare 存 name 的时候存放方式有问题导致 name 最后没有结束符,于是回头看一下set_name 的代码,于是发现以下代码问题:
1 2 3 4 5 6 7 |
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; } # 箭头处发现存 name 时候申请的内存长度为 name_arg.length,没有把最后的 \0 一起存放进去,导致最后少了结束符,这就有概率导致查找 name 出错。 |
于是把 name_arg.length 改为 name_arg.length+1,重新编译代码问题解决。
c++ 中字符串的使用一定要注意最后的结束符\0
,如果因为少分配了一个长度导致结束符没有存进去,最后存放的字符串就会产生问题。
到此这篇关于MySQL的prepare使用及遇到bug解析过程的文章就介绍到这了
2023-10-30
windows上的mysql服务突然消失提示10061 Unkonwn error问题及解决方案2023-10-30
MySQL非常重要的日志bin log详解2023-10-30
详解MySQL事务日志redo log一、单表查询 1、排序 2、聚合函数 3、分组 4、limit 二、SQL约束 1、主键约束 2、非空约束 3、唯一约束 4、外键约束 5、默认值 三、多表查询 1、内连接 1)隐式内连接: 2)显式内连接: 2、外连接 1)左外连接 2)右外连接 四...
2023-10-30
Mysql删除表重复数据 表里存在唯一主键 没有主键时删除重复数据 Mysql删除表中重复数据并保留一条 准备一张表 用的是mysql8 大家自行更改 创建表并添加四条相同的数据...
2023-10-30