美文网首页mysql
MySQL 的prepare使用中的bug解析过程

MySQL 的prepare使用中的bug解析过程

作者: GreatSQL | 来源:发表于2022-05-07 10:13 被阅读0次
  • GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源。

[toc]

一、问题发现

在一次开发中使用 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解析过程

    GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源。 [toc] 一、问题发现 在一次开发...

  • 存储过程

    创建存储过程 mysql: 执行存储过程 mysql: 使用OUT参数和INOUT参数: PREPARE和EXEC...

  • 开发中,你一般是如何调适bug的

    bug分为线上的bug和测试中的bug 对于线上的bug 项目使用了友盟统计,因此会有崩溃日志,通过解析dYSM可...

  • sql目录

    记录MySQL使用过程中(工作或者开发过程中遇到的问题) MySQL导出文件中遇到的问题 MySQL使用索引提高查...

  • MySQL存储过程使用解析

    mysql存储过程 存储过程(Stored Procedure)是一种在数据库中存储复杂程序,以便外部程序调用的一...

  • mysql 技术点汇总

    MYSQL存储引擎介绍 索引 mysql 执行过程 mysql 语句解析过程 https://www.cnblog...

  • mysql的prepare的使用场景

    今天面试官问道:mysql(或者pg)为啥要使用prepare?答:· 在编代码前,您想要测试预制语句在您的应用程...

  • Mysql的奇淫怪技

    在项目开发的过程中,包括测试人员提出的一些bug,慢慢总结出一些Mysql的使用经验。 1. decimal类型的...

  • iOS SQlite3 常用命令

    使用的过程根据使用的函数大致分为如下几个过程: sqlite3_open() sqlite3_prepare() ...

  • SQLite3过程中的函数详解

    使用的过程根据使用的函数大致分为如下几个过程: sqlite3_open() sqlite3_prepare() ...

网友评论

    本文标题:MySQL 的prepare使用中的bug解析过程

    本文链接:https://www.haomeiwen.com/subject/piwturtx.html