MySQL 的prepare使用中的bug解析过程
- GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源。
一、问题发现
在一次开发中使用 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: %.*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 设置的函数。
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最后不是 \0 结束,而是别的字符。
3、接着到 execute 的函数看一下这个 name 值,发现确实后面跟的不是 \0 结束符,而是变为乱码。于是这里当然会报错找不到该 stmt name 了。
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 的代码,于是发现以下代码问题:
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,如果因为少分配了一个长度导致结束符没有存进去,最后存放的字符串就会产生问题。
Enjoy GreatSQL
本文由博客一文多发平台 OpenWrite 发布!
MySQL 的prepare使用中的bug解析过程的更多相关文章
- MySQL解析过程、执行过程
转载:https://student-lp.iteye.com/blog/2152601 https://www.cnblogs.com/cdf-opensource-007/p/6502556.ht ...
- 年年出妖事,一例由JSON解析导致的"薛定谔BUG"排查过程记录
前言 做开发这么多年,也碰到无数的bug了.不过再复杂的bug,只要仔细去研读代码,加上debug,总能找到原因. 但是最近公司内碰到的这一个bug,这个bug初看很简单,但是非常妖孽,在一段时间内我 ...
- MyBatis 源码分析 - 配置文件解析过程
* 本文速览 由于本篇文章篇幅比较大,所以这里拿出一节对本文进行快速概括.本篇文章对 MyBatis 配置文件中常用配置的解析过程进行了较为详细的介绍和分析,包括但不限于settings,typeAl ...
- Mysql 预处理 PREPARE以及预处理的好处
Mysql 预处理 PREPARE以及预处理的好处 Mysql手册 预处理记载: 预制语句的SQL语法在以下情况下使用: · 在编代码前,您想要测试预制语句在您的应用程序中运行得如何.或者也许一个 ...
- 【原创】大数据基础之Hive(2)Hive SQL执行过程之SQL解析过程
Hive SQL解析过程 SQL->AST(Abstract Syntax Tree)->Task(MapRedTask,FetchTask)->QueryPlan(Task集合)- ...
- MyBatis 源码分析 - 映射文件解析过程
1.简介 在上一篇文章中,我详细分析了 MyBatis 配置文件的解析过程.由于上一篇文章的篇幅比较大,加之映射文件解析过程也比较复杂的原因.所以我将映射文件解析过程的分析内容从上一篇文章中抽取出来, ...
- spring cron表达式及解析过程
1.cron表达式 cron表达式是用来配置spring定时任务执行时间的字符串,由5个空格分隔成的6个域构成,格式如下: {秒} {分} {时} {日} {月} {周} 每一个域的含义解释 ...
- 常见bug解析-移动端
手机测试常见bug解析 1.测试时遇到“手机无响应”? 有以下几个原因: a.手机内存不足 b.android进程之间死锁引起的(就是两个进程之间) c.手机的CPU运行高引起的 可以查看手机的崩溃日 ...
- MySQL 利用xtrabackup进行增量备份详细过程汇总 (转)
Xtrabackup下载.安装以及全量备份请参考:http://blog.itpub.net/26230597/viewspace-1465772/ 1,创建mysql备份用户 mysql -uroo ...
随机推荐
- springMvc和Hibernate集成实现用户添加
源码:http://pan.baidu.com/s/1i4xVLE9(百度云) 步骤:一.创建数据库(mysql) 二.导入相应jar包(注意不同数据库jdbc.jar包)配置web.xml.spri ...
- 283. Move Zeroes - LeetCode
Question 283. Move Zeroes Solution 题目大意:将0移到最后 思路: 1. 数组复制 2. 不用数组复制 Java实现: 数组复制 public void moveZe ...
- 108_Power Pivot购物篮分析分组GENERATE之笛卡尔积、排列、组合
博客:www.jiaopengzi.com 焦棚子的文章目录 请点击下载附件 1.背景 昨天在看论坛帖子时候(帖子),看到一个关于SKU组合的问题,有很多M大佬都给出了处理方案,于是想用dax也写一个 ...
- 联盟链 Hyperledger Fabric 应用场景
一.说明 本文主要通过一个例子分享以 Hyperledger Fabric 为代表的联盟链应用场景. 关于 Fabric 的相关概念请先参考文章 <Hyperledger Fabric 核心概念 ...
- MUI+html5的plus.webview页面传值在电脑浏览器上不可见
使用plus.webview.currentWebview() 获得当前窗口的webview对象后,再使用document.write()输出显示webview的某个属性值,而plus.webview ...
- 将Abp移植进.NET MAUI项目(一):搭建项目
前言 去年12月份做了MAUI混合开发框架的调研,想起来文章里给自己挖了个坑,要教大家如何把Abp移植进Maui项目,由于篇幅限制,将分为三个章节. 将Abp移植进.NET MAUI项目(一):搭 ...
- 对抗噪音,一键清晰,HMS Core音频编辑服务给你“录音棚”般的体验
短视频时代来临,一部手机就可以玩转多种花样,所以越来越多的自由创作者加入这个行业,平时生活中用手机拍短视频.街头唱歌的非专业从业者随处可见.离开了录音棚,没有专业.统一的录音设备,无论在家里还是在路边 ...
- dubbo是如何实现可扩展的?(二)
牛逼的框架,看似复杂难懂,思路其实很清晰.---me 上篇文章,在整体扩展思路上进行了源码分析,比较粗糙,现在就某些点再详细梳理下. dubbo SPi的扩展,基于一类.三注解. 一类是Extensi ...
- [补漏]shift&算法
题意:regular number 给你一个字符串,要你输出所有(每位都符合要求的)子串,输入时告诉你每位只能填的数集. 思路: bitsetc[x]存每个数字可以存在的字符串位的二进制集合.(如3可 ...
- 产品揭秘】来也Lead 2022产品亮点解读-RPA学习天地
2022年4月26日,来也举行新品发布会.作为技术人员,花里胡哨的我且不说,我且说技术相关.整体架构"概念"整个平台覆盖了智能自动化的全生命周期包含:业务理解.流程创建.随处运行. ...