• GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源。

一、问题发现

在一次开发中使用 MySQL PREPARE 以后,从 prepare 直接取 name 赋值给 lex->prepared_stmt_name 然后给 EXECUTE 用,发现有一定概率找不到 prepare stmt 的 name,于是开始动手调查问题发生的原因。

SQL语句示例:

  1. CREATE TABLE t1 (a INT, b VARCHAR(10));
  2. PREPARE dbms_sql_stmt4 FROM 'INSERT INTO t1 VALUES (1,''11'')';
  3. EXECUTE dbms_sql_stmt4;
  4. 报错:
  5. 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. void MySQL_sql_stmt_execute(THD *thd) {
  2. LEX *lex = thd->lex;
  3. const LEX_CSTRING &name = lex->prepared_stmt_name;
  4. DBUG_TRACE;
  5. DBUG_PRINT("info", ("EXECUTE: %.*s\n", (int)name.length, name.str));
  6. Prepared_statement *stmt;
  7. if (!(stmt = thd->stmt_map.find_by_name(name))) {
  8. my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), static_cast<int>(name.length),
  9. name.str, "EXECUTE");
  10. return;
  11. }

2、这个 lex->prepared_stmt_name 是从 prepare name 中赋值的,于是调查 prepare 这个 name 设置的函数。

  1. bool Prepared_statement::set_name(const LEX_CSTRING &name_arg) {
  2. m_name.length = name_arg.length;
  3. m_name.str = static_cast<char *>(
  4. memdup_root(m_arena.mem_root, name_arg.str, name_arg.length));
  5. return m_name.str == nullptr;
  6. }

gdb 跟踪代码:

  1. Thread 46 "MySQLd" hit Breakpoint 1, Prepared_statement::set_name (this=0x7fff2cbf3250, name_arg=...)
  2. at /home/wuyy/greatdb/gitmerge/percona-server/sql/sql_prepare.cc:2447
  3. 2447 bool Prepared_statement::set_name(const LEX_CSTRING &name_arg) {
  4. (gdb) n
  5. 2448 m_name.length = name_arg.length;
  6. (gdb)
  7. 2450 memdup_root(m_arena.mem_root, name_arg.str, name_arg.length));
  8. (gdb)
  9. 2449 m_name.str = static_cast<char *>(
  10. (gdb)
  11. 2451 return m_name.str == nullptr;
  12. (gdb) p m_name
  13. $9 = {
  14. str = 0x7fff2cd09a68 "dbms_sql_stmt4", '\217' <repeats 98 times>, "FLOAT",
  15. length = 14
  16. # 可以看到 m_name 后面出现了乱码,说明 m_nam e最后不是 \0 结束,而是别的字符。

3、接着到 execute 的函数看一下这个 name 值,发现确实后面跟的不是 \0 结束符,而是变为乱码。于是这里当然会报错找不到该 stmt name 了。

  1. Thread 46 "MySQLd" hit Breakpoint 2, MySQL_sql_stmt_execute (thd=0x7fff2c002688)
  2. at /home/wuyy/greatdb/gitmerge/percona-server/sql/sql_prepare.cc:1944
  3. 1944 void MySQL_sql_stmt_execute(THD *thd) {
  4. (gdb) n
  5. 1945 LEX *lex = thd->lex;
  6. (gdb)
  7. 1946 const LEX_CSTRING &name = lex->prepared_stmt_name;
  8. (gdb)
  9. 1947 DBUG_TRACE;
  10. (gdb) p name
  11. $10 = (const LEX_CSTRING &) @0x7fff2cd501e0: {
  12. str = 0x7fff2cd09a68 "dbms_sql_stmt4\217\217p\271\221]UU",
  13. length = 22
  14. }
  15. (gdb) n
  16. 1948 DBUG_PRINT("info", ("EXECUTE: %.*s\n", (int)name.length, name.str));
  17. (gdb)
  18. 1951 if (!(stmt = thd->stmt_map.find_by_name(name))) {
  19. (gdb)
  20. 1953 name.str, "EXECUTE");
  21. (gdb)
  22. 1952 my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), static_cast<int>(name.length),
  23. (gdb)
  24. 1954 return;
  25. # 结果报错了。

三、问题解决方案

通过以上 gdb 跟踪过程我们可以发现 prepare 存 name 的时候存放方式有问题导致 name 最后没有结束符,于是回头看一下set_name 的代码,于是发现以下代码问题:

  1. bool Prepared_statement::set_name(const LEX_CSTRING &name_arg) {
  2. m_name.length = name_arg.length;
  3. m_name.str = static_cast<char *>(
  4. memdup_root(m_arena.mem_root, name_arg.str, name_arg.length));←这里问题
  5. return m_name.str == nullptr;
  6. }
  7. # 箭头处发现存 name 时候申请的内存长度为 name_arg.length,没有把最后的 \0 一起存放进去,导致最后少了结束符,这就有概率导致查找 name 出错。

于是把 name_arg.length 改为 name_arg.length+1,重新编译代码问题解决。

四、问题总结

c++ 中字符串的使用一定要注意最后的结束符\0,如果因为少分配了一个长度导致结束符没有存进去,最后存放的字符串就会产生问题。

Enjoy GreatSQL

本文由博客一文多发平台 OpenWrite 发布!

MySQL 的prepare使用中的bug解析过程的更多相关文章

  1. MySQL解析过程、执行过程

    转载:https://student-lp.iteye.com/blog/2152601 https://www.cnblogs.com/cdf-opensource-007/p/6502556.ht ...

  2. 年年出妖事,一例由JSON解析导致的"薛定谔BUG"排查过程记录

    前言 做开发这么多年,也碰到无数的bug了.不过再复杂的bug,只要仔细去研读代码,加上debug,总能找到原因. 但是最近公司内碰到的这一个bug,这个bug初看很简单,但是非常妖孽,在一段时间内我 ...

  3. MyBatis 源码分析 - 配置文件解析过程

    * 本文速览 由于本篇文章篇幅比较大,所以这里拿出一节对本文进行快速概括.本篇文章对 MyBatis 配置文件中常用配置的解析过程进行了较为详细的介绍和分析,包括但不限于settings,typeAl ...

  4. Mysql 预处理 PREPARE以及预处理的好处

    Mysql 预处理 PREPARE以及预处理的好处 Mysql手册 预处理记载: 预制语句的SQL语法在以下情况下使用:   · 在编代码前,您想要测试预制语句在您的应用程序中运行得如何.或者也许一个 ...

  5. 【原创】大数据基础之Hive(2)Hive SQL执行过程之SQL解析过程

    Hive SQL解析过程 SQL->AST(Abstract Syntax Tree)->Task(MapRedTask,FetchTask)->QueryPlan(Task集合)- ...

  6. MyBatis 源码分析 - 映射文件解析过程

    1.简介 在上一篇文章中,我详细分析了 MyBatis 配置文件的解析过程.由于上一篇文章的篇幅比较大,加之映射文件解析过程也比较复杂的原因.所以我将映射文件解析过程的分析内容从上一篇文章中抽取出来, ...

  7. spring cron表达式及解析过程

    1.cron表达式 cron表达式是用来配置spring定时任务执行时间的字符串,由5个空格分隔成的6个域构成,格式如下: {秒}  {分}  {时}  {日}  {月}  {周} 每一个域的含义解释 ...

  8. 常见bug解析-移动端

    手机测试常见bug解析 1.测试时遇到“手机无响应”? 有以下几个原因: a.手机内存不足 b.android进程之间死锁引起的(就是两个进程之间) c.手机的CPU运行高引起的 可以查看手机的崩溃日 ...

  9. MySQL 利用xtrabackup进行增量备份详细过程汇总 (转)

    Xtrabackup下载.安装以及全量备份请参考:http://blog.itpub.net/26230597/viewspace-1465772/ 1,创建mysql备份用户 mysql -uroo ...

随机推荐

  1. 论文阅读 Dynamic Network Embedding by Modeling Triadic Closure Process

    3 Dynamic Network Embedding by Modeling Triadic Closure Process link:https://scholar.google.com.sg/s ...

  2. 关于JNPF3.4版本的三大改变,你真的了解了吗?

  3. 【转】理解 CI 和 CD 之间的区别

    有很多关于持续集成(CI)和持续交付(CD)的资料.很多文章用技术术语来进行解释,以及它们怎么帮助你的组织.可惜的是,在一些情况下,这些方法通常与特定工具.甚至供应商相关联.在公司食堂里非常常见的谈话 ...

  4. ElasticSearch6.4.2

    做一个简单的API记录 1.依赖为6.4.2  比较老的版本 2.指定ES集群,可接多个Put(); Setting setting=Setting.builder().put("clust ...

  5. Mac安装Brew包管理系统

    Mac安装Brew包管理系统 前言 为什么需要安装brew 作为一个开发人员, 习惯了使用centos的yum和ubuntu的apt, 在mac中有没有这两个工具的平替? 有, 就是Brew. Bre ...

  6. Windows启动谷歌浏览器Chrome失败(应用程序无法启动,因为应用程序的并行配置不正确)解决方法

    目录 一.系统环境 二.问题描述 三.解决方法 一.系统环境 Windows版本 系统类型 浏览器Chrome版本 Windows 10 专业版 64 位操作系统, 基于 x64 的处理器 版本 10 ...

  7. Java 图片生成PDF

    public static void main(String[] args) { String imageFolderPath = "E:\\Tencet\\图片\\test\\" ...

  8. windows server2012R2 上 .net core IIS 部署--应用程序池 自动停止

    在windows server2016安装部署.NET CORE时,只需要将.net core应用程序池设置无托管,然后对应你项目的版本安装一个dotnet-hosting-2.2.6-win.exe ...

  9. HDFS存储目录分析

    一.介绍 HDFS metadata以树状结构存储整个HDFS上的文件和目录,以及相应的权限.配额和副本因子(replication factor)等.本文基于Hadoop2.6版本介绍HDFS Na ...

  10. 没想到吧,Spring中还有一招集合注入的写法

    原创:微信公众号 码农参上,欢迎分享,转载请保留出处. 哈喽大家好啊,我是Hydra. Spring作为项目中不可缺少的底层框架,提供的最基础的功能就是bean的管理了.bean的注入相信大家都比较熟 ...