MySQL的prepare使用及遇到bug解析過程
一、問題發(fā)現(xiàn)
在一次開發(fā)中使用 MySQL PREPARE 以后,從 prepare 直接取 name 賦值給 lex->prepared_stmt_name 然后給 EXECUTE 用,發(fā)現(xiàn)有一定概率找不到 prepare stmt 的 name,于是開始動手調(diào)查問題發(fā)生的原因。
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
二、問題調(diào)查過程
1、根據(jù)報錯信息找到對應(yīng)源碼,發(fā)現(xiàn)在MySQL_sql_stmt_execute
里面有判斷當(dāng)找不到 stmt name
時候報錯信息。
這里的 name
此時已經(jīng)是亂碼了。
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
中賦值的,于是調(diào)查 prepare 這個 name 設(shè)置的函數(shù)。
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 后面出現(xiàn)了亂碼,說明 m_nam e最后不是 \0 結(jié)束,而是別的字符。
3、接著到 execute
的函數(shù)看一下這個 name
值,發(fā)現(xiàn)確實(shí)后面跟的不是 \0
結(jié)束符,而是變?yōu)閬y碼。于是這里當(dāng)然會報錯找不到該 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; # 結(jié)果報錯了。
三、問題解決方案
通過以上 gdb 跟蹤過程我們可以發(fā)現(xiàn) prepare 存 name 的時候存放方式有問題導(dǎo)致 name 最后沒有結(jié)束符,于是回頭看一下set_name 的代碼,于是發(fā)現(xiàn)以下代碼問題:
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; } # 箭頭處發(fā)現(xiàn)存 name 時候申請的內(nèi)存長度為 name_arg.length,沒有把最后的 \0 一起存放進(jìn)去,導(dǎo)致最后少了結(jié)束符,這就有概率導(dǎo)致查找 name 出錯。
于是把 name_arg.length 改為 name_arg.length+1,重新編譯代碼問題解決。
四、問題總結(jié)
c++ 中字符串的使用一定要注意最后的結(jié)束符\0
,如果因為少分配了一個長度導(dǎo)致結(jié)束符沒有存進(jìn)去,最后存放的字符串就會產(chǎn)生問題。
到此這篇關(guān)于MySQL的prepare使用及遇到bug解析過程的文章就介紹到這了,更多相關(guān)mysql prepare使用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
MySQL數(shù)據(jù)庫開啟、關(guān)閉、查看函數(shù)功能的方法
這篇文章主要介紹了MySQL數(shù)據(jù)庫開啟、關(guān)閉、查看函數(shù)功能的方法,本文為解決一個錯誤總結(jié)而來,錯誤信息本文一同給出,需要的朋友可以參考下2014-10-10MySQL結(jié)合使用數(shù)據(jù)庫分析工具SchemaSpy的方法
這篇文章主要介紹了MySQL結(jié)合使用數(shù)據(jù)庫分析工具SchemaSpy的方法,需要的朋友可以參考下2015-06-06SQL中distinct 和 row_number() over() 的區(qū)別及用法
這篇文章主要介紹了SQL中distinct 和 row_number() over() 的區(qū)別及用法的相關(guān)資料,需要的朋友可以參考下2017-03-03實(shí)例驗證MySQL|update字段為相同的值是否會記錄binlog
這篇文章主要介紹了實(shí)例驗證MySQL|update字段為相同的值是否會記錄binlog,幫助大家更好的理解和學(xué)習(xí)MySQL數(shù)據(jù)庫,感興趣的朋友可以了解下2020-10-10MySQL同步數(shù)據(jù)Replication的實(shí)現(xiàn)步驟
本文主要介紹了MySQL同步數(shù)據(jù)Replication的實(shí)現(xiàn)步驟,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-03-03